/Users/deen/code/yugabyte-db/src/yb/integration-tests/ts_tablet_manager-itest.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 <string> |
35 | | |
36 | | #include <gtest/gtest.h> |
37 | | |
38 | | #include "yb/client/client.h" |
39 | | #include "yb/client/schema.h" |
40 | | #include "yb/client/table_creator.h" |
41 | | |
42 | | #include "yb/common/partition.h" |
43 | | |
44 | | #include "yb/consensus/consensus.h" |
45 | | #include "yb/consensus/consensus.proxy.h" |
46 | | #include "yb/consensus/metadata.pb.h" |
47 | | #include "yb/consensus/quorum_util.h" |
48 | | |
49 | | #include "yb/fs/fs_manager.h" |
50 | | |
51 | | #include "yb/gutil/strings/substitute.h" |
52 | | |
53 | | #include "yb/integration-tests/cluster_itest_util.h" |
54 | | #include "yb/integration-tests/mini_cluster.h" |
55 | | |
56 | | #include "yb/master/master_cluster.proxy.h" |
57 | | #include "yb/master/master_heartbeat.pb.h" |
58 | | #include "yb/master/mini_master.h" |
59 | | |
60 | | #include "yb/rpc/messenger.h" |
61 | | #include "yb/rpc/proxy.h" |
62 | | |
63 | | #include "yb/server/server_base.proxy.h" |
64 | | |
65 | | #include "yb/tablet/tablet_peer.h" |
66 | | |
67 | | #include "yb/tserver/mini_tablet_server.h" |
68 | | #include "yb/tserver/tablet_server.h" |
69 | | #include "yb/tserver/ts_tablet_manager.h" |
70 | | #include "yb/tserver/tserver_admin.proxy.h" |
71 | | #include "yb/tserver/tserver_service.proxy.h" |
72 | | |
73 | | #include "yb/util/test_util.h" |
74 | | |
75 | | DECLARE_bool(enable_leader_failure_detection); |
76 | | DECLARE_bool(catalog_manager_wait_for_new_tablets_to_elect_leader); |
77 | | DEFINE_int32(num_election_test_loops, 3, |
78 | | "Number of random EmulateElection() loops to execute in " |
79 | | "TestReportNewLeaderOnLeaderChange"); |
80 | | DECLARE_bool(enable_ysql); |
81 | | DECLARE_bool(use_create_table_leader_hint); |
82 | | |
83 | | namespace yb { |
84 | | namespace tserver { |
85 | | |
86 | | using client::YBClient; |
87 | | using client::YBSchema; |
88 | | using client::YBTable; |
89 | | using client::YBTableCreator; |
90 | | using client::YBTableName; |
91 | | using consensus::GetConsensusRole; |
92 | | using consensus::RaftPeerPB; |
93 | | using itest::SimpleIntKeyYBSchema; |
94 | | using master::ReportedTabletPB; |
95 | | using master::TabletReportPB; |
96 | | using rpc::Messenger; |
97 | | using rpc::MessengerBuilder; |
98 | | using strings::Substitute; |
99 | | using tablet::TabletPeer; |
100 | | using tserver::MiniTabletServer; |
101 | | using tserver::TSTabletManager; |
102 | | |
103 | | static const YBTableName kTableName(YQL_DATABASE_CQL, "my_keyspace", "test-table"); |
104 | | static const int kNumReplicas = 3; |
105 | | |
106 | | class TsTabletManagerITest : public YBTest { |
107 | | public: |
108 | | TsTabletManagerITest() |
109 | 1 | : schema_(SimpleIntKeyYBSchema()) { |
110 | 1 | } |
111 | | void SetUp() override; |
112 | | void TearDown() override; |
113 | | |
114 | | protected: |
115 | | const YBSchema schema_; |
116 | | |
117 | | std::unique_ptr<MiniCluster> cluster_; |
118 | | std::unique_ptr<Messenger> client_messenger_; |
119 | | std::unique_ptr<YBClient> client_; |
120 | | }; |
121 | | |
122 | 1 | void TsTabletManagerITest::SetUp() { |
123 | | // We don't need the transaction status table to be created. |
124 | 1 | SetAtomicFlag(false, &FLAGS_enable_ysql); |
125 | 1 | YBTest::SetUp(); |
126 | | |
127 | 1 | MessengerBuilder bld("client"); |
128 | 1 | client_messenger_ = ASSERT_RESULT(bld.Build()); |
129 | 1 | client_messenger_->TEST_SetOutboundIpBase(ASSERT_RESULT(HostToAddress("127.0.0.1"))); |
130 | | |
131 | 1 | MiniClusterOptions opts; |
132 | 1 | opts.num_tablet_servers = kNumReplicas; |
133 | 1 | cluster_.reset(new MiniCluster(opts)); |
134 | 1 | ASSERT_OK(cluster_->Start()); |
135 | 0 | client_ = ASSERT_RESULT(cluster_->CreateClient(client_messenger_.get())); |
136 | 0 | } |
137 | | |
138 | 1 | void TsTabletManagerITest::TearDown() { |
139 | 1 | client_.reset(); |
140 | 1 | client_messenger_->Shutdown(); |
141 | 1 | cluster_->Shutdown(); |
142 | 1 | YBTest::TearDown(); |
143 | 1 | } |
144 | | |
145 | | // Test that when the leader changes, the tablet manager gets notified and |
146 | | // includes that information in the next tablet report. |
147 | 0 | TEST_F(TsTabletManagerITest, TestReportNewLeaderOnLeaderChange) { |
148 | | // We need to control elections precisely for this test since we're using |
149 | | // EmulateElection() with a distributed consensus configuration. |
150 | 0 | FLAGS_enable_leader_failure_detection = false; |
151 | 0 | FLAGS_catalog_manager_wait_for_new_tablets_to_elect_leader = false; |
152 | 0 | FLAGS_use_create_table_leader_hint = false; |
153 | | |
154 | | // Run a few more iters in slow-test mode. |
155 | 0 | OverrideFlagForSlowTests("num_election_test_loops", "10"); |
156 | |
|
157 | 0 | ASSERT_OK(client_->CreateNamespaceIfNotExists(kTableName.namespace_name(), |
158 | 0 | kTableName.namespace_type())); |
159 | | // Create the table. |
160 | 0 | std::shared_ptr<YBTable> table; |
161 | 0 | std::unique_ptr<YBTableCreator> table_creator(client_->NewTableCreator()); |
162 | 0 | ASSERT_OK(table_creator->table_name(kTableName) |
163 | 0 | .schema(&schema_) |
164 | 0 | .hash_schema(YBHashSchema::kMultiColumnHash) |
165 | 0 | .num_tablets(1) |
166 | 0 | .Create()); |
167 | 0 | ASSERT_OK(client_->OpenTable(kTableName, &table)); |
168 | |
|
169 | 0 | rpc::ProxyCache proxy_cache(client_messenger_.get()); |
170 | | |
171 | | // Build a TServerDetails map so we can check for convergence. |
172 | 0 | master::MasterClusterProxy master_proxy(&proxy_cache, cluster_->mini_master()->bound_rpc_addr()); |
173 | |
|
174 | 0 | auto ts_map = ASSERT_RESULT(itest::CreateTabletServerMap(master_proxy, &proxy_cache)); |
175 | | |
176 | | // Collect the tablet peers so we get direct access to consensus. |
177 | 0 | vector<std::shared_ptr<TabletPeer> > tablet_peers; |
178 | 0 | for (int replica = 0; replica < kNumReplicas; replica++) { |
179 | 0 | MiniTabletServer* ts = cluster_->mini_tablet_server(replica); |
180 | 0 | ts->FailHeartbeats(); // Stop heartbeating we don't race against the Master. |
181 | 0 | vector<std::shared_ptr<TabletPeer> > cur_ts_tablet_peers; |
182 | | // The replicas may not have been created yet, so loop until we see them. |
183 | 0 | while (true) { |
184 | 0 | cur_ts_tablet_peers = ts->server()->tablet_manager()->GetTabletPeers(); |
185 | 0 | if (!cur_ts_tablet_peers.empty()) break; |
186 | 0 | SleepFor(MonoDelta::FromMilliseconds(10)); |
187 | 0 | } |
188 | 0 | ASSERT_EQ(1, cur_ts_tablet_peers.size()); |
189 | 0 | ASSERT_OK(cur_ts_tablet_peers[0]->WaitUntilConsensusRunning(MonoDelta::FromSeconds(10))); |
190 | 0 | tablet_peers.push_back(cur_ts_tablet_peers[0]); |
191 | 0 | } |
192 | | |
193 | | // Loop and cause elections and term changes from different servers. |
194 | | // TSTabletManager should acknowledge the role changes via tablet reports. |
195 | 0 | unsigned int seed = SeedRandom(); |
196 | 0 | for (int i = 0; i < FLAGS_num_election_test_loops; i++) { |
197 | 0 | SCOPED_TRACE(Substitute("Iter: $0", i)); |
198 | 0 | int new_leader_idx = rand_r(&seed) % 2; |
199 | 0 | LOG(INFO) << "Electing peer " << new_leader_idx << "..."; |
200 | 0 | consensus::Consensus* con = CHECK_NOTNULL(tablet_peers[new_leader_idx]->consensus()); |
201 | 0 | ASSERT_OK(con->EmulateElection()); |
202 | 0 | LOG(INFO) << "Waiting for servers to agree..."; |
203 | 0 | ASSERT_OK(WaitForServersToAgree(MonoDelta::FromSeconds(5), |
204 | 0 | ts_map, tablet_peers[0]->tablet_id(), i + 1)); |
205 | | |
206 | | // Now check that the tablet report reports the correct role for both servers. |
207 | 0 | for (int replica = 0; replica < kNumReplicas; replica++) { |
208 | | // The MarkDirty() callback is on an async thread so it might take the |
209 | | // follower a few milliseconds to execute it. Wait for that to happen. |
210 | 0 | TSTabletManager* tablet_manager = |
211 | 0 | cluster_->mini_tablet_server(replica)->server()->tablet_manager(); |
212 | 0 | for (int retry = 0; retry <= 12; retry++) { |
213 | 0 | if (tablet_manager->TEST_GetNumDirtyTablets() > 0) break; |
214 | 0 | SleepFor(MonoDelta::FromMilliseconds(1 << retry)); |
215 | 0 | } |
216 | | |
217 | | // Ensure that our tablet reports are consistent. |
218 | 0 | TabletReportPB report; |
219 | 0 | tablet_manager->GenerateTabletReport(&report); |
220 | 0 | ASSERT_EQ(1, report.updated_tablets_size()) << "Wrong report size:\n" << report.DebugString(); |
221 | 0 | ReportedTabletPB reported_tablet = report.updated_tablets(0); |
222 | 0 | ASSERT_TRUE(reported_tablet.has_committed_consensus_state()); |
223 | |
|
224 | 0 | string uuid = tablet_peers[replica]->permanent_uuid(); |
225 | 0 | PeerRole role = GetConsensusRole(uuid, reported_tablet.committed_consensus_state()); |
226 | 0 | if (replica == new_leader_idx) { |
227 | 0 | ASSERT_EQ(PeerRole::LEADER, role) |
228 | 0 | << "Tablet report: " << report.ShortDebugString(); |
229 | 0 | } else { |
230 | 0 | ASSERT_EQ(PeerRole::FOLLOWER, role) |
231 | 0 | << "Tablet report: " << report.ShortDebugString(); |
232 | 0 | } |
233 | 0 | } |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | } // namespace tserver |
238 | | } // namespace yb |