/Users/deen/code/yugabyte-db/src/yb/integration-tests/master_replication-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 <functional> |
34 | | #include <memory> |
35 | | #include <string> |
36 | | #include <unordered_map> |
37 | | #include <unordered_set> |
38 | | #include <utility> |
39 | | #include <vector> |
40 | | |
41 | | #include <boost/optional/optional.hpp> |
42 | | #include <glog/logging.h> |
43 | | #include <gtest/gtest.h> |
44 | | |
45 | | #include "yb/client/client.h" |
46 | | #include "yb/client/schema.h" |
47 | | #include "yb/client/table_creator.h" |
48 | | |
49 | | #include "yb/common/column_id.h" |
50 | | #include "yb/common/common.pb.h" |
51 | | #include "yb/common/entity_ids_types.h" |
52 | | #include "yb/common/hybrid_time.h" |
53 | | #include "yb/common/partition.h" |
54 | | |
55 | | #include "yb/gutil/algorithm.h" |
56 | | |
57 | | #include "yb/integration-tests/mini_cluster.h" |
58 | | #include "yb/integration-tests/yb_mini_cluster_test_base.h" |
59 | | |
60 | | #include "yb/master/master.h" |
61 | | #include "yb/master/mini_master.h" |
62 | | |
63 | | #include "yb/util/memory/arena_fwd.h" |
64 | | #include "yb/util/status.h" |
65 | | #include "yb/util/status_log.h" |
66 | | #include "yb/util/test_util.h" |
67 | | #include "yb/util/thread.h" |
68 | | #include "yb/util/uuid.h" |
69 | | |
70 | | using std::vector; |
71 | | |
72 | | namespace yb { |
73 | | namespace master { |
74 | | |
75 | | using client::YBClient; |
76 | | using client::YBClientBuilder; |
77 | | using client::YBColumnSchema; |
78 | | using client::YBSchema; |
79 | | using client::YBSchemaBuilder; |
80 | | using client::YBTable; |
81 | | using client::YBTableCreator; |
82 | | using client::YBTableName; |
83 | | using std::shared_ptr; |
84 | | |
85 | | const std::string kKeyspaceName("my_keyspace"); |
86 | | const YBTableName kTableName1(YQL_DATABASE_CQL, kKeyspaceName, "testMasterReplication-1"); |
87 | | const YBTableName kTableName2(YQL_DATABASE_CQL, kKeyspaceName, "testMasterReplication-2"); |
88 | | |
89 | | const int kNumTabletServerReplicas = 3; |
90 | | |
91 | | class MasterReplicationTest : public YBMiniClusterTestBase<MiniCluster> { |
92 | | public: |
93 | 4 | MasterReplicationTest() { |
94 | 4 | opts_.num_masters = num_masters_ = 3; |
95 | 4 | opts_.num_tablet_servers = kNumTabletServerReplicas; |
96 | 4 | } |
97 | | |
98 | 4 | void SetUp() override { |
99 | 4 | YBMiniClusterTestBase::SetUp(); |
100 | 4 | cluster_.reset(new MiniCluster(opts_)); |
101 | 4 | ASSERT_OK(cluster_->Start()); |
102 | 0 | ASSERT_OK(cluster_->WaitForTabletServerCount(kNumTabletServerReplicas)); |
103 | 0 | } |
104 | | |
105 | 1 | void DoTearDown() override { |
106 | 1 | if (cluster_) { |
107 | 1 | cluster_->Shutdown(); |
108 | 1 | cluster_.reset(); |
109 | 1 | } |
110 | 1 | YBMiniClusterTestBase::DoTearDown(); |
111 | 1 | } |
112 | | |
113 | | // This method is meant to be run in a separate thread. |
114 | 0 | void StartClusterDelayed(int64_t micros) { |
115 | 0 | LOG(INFO) << "Sleeping for " << micros << " micro seconds..."; |
116 | 0 | SleepFor(MonoDelta::FromMicroseconds(micros)); |
117 | 0 | LOG(INFO) << "Attempting to start the cluster..."; |
118 | 0 | CHECK_OK(cluster_->Start()); |
119 | 0 | CHECK_OK(cluster_->WaitForTabletServerCount(kNumTabletServerReplicas)); |
120 | 0 | } |
121 | | |
122 | 0 | void ListMasterServerAddrs(vector<string>* out) { |
123 | 0 | for (int i = 0; i < num_masters_; i++) { |
124 | 0 | out->push_back(cluster_->mini_master(i)->bound_rpc_addr_str()); |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | 0 | Result<std::unique_ptr<client::YBClient>> CreateClient() { |
129 | 0 | YBClientBuilder builder; |
130 | 0 | for (int i = 0; i < num_masters_; i++) { |
131 | 0 | if (!cluster_->mini_master(i)->master()->IsShutdown()) { |
132 | 0 | builder.add_master_server_addr(cluster_->mini_master(i)->bound_rpc_addr_str()); |
133 | 0 | } |
134 | 0 | } |
135 | |
|
136 | 0 | auto client = VERIFY_RESULT(builder.Build()); |
137 | 0 | RETURN_NOT_OK(client->CreateNamespaceIfNotExists(kKeyspaceName)); |
138 | 0 | return client; |
139 | 0 | } |
140 | | |
141 | | Status CreateTable(YBClient* client, |
142 | 0 | const YBTableName& table_name) { |
143 | 0 | YBSchema schema; |
144 | 0 | YBSchemaBuilder b; |
145 | 0 | b.AddColumn("key")->Type(INT32)->NotNull()->PrimaryKey(); |
146 | 0 | b.AddColumn("int_val")->Type(INT32)->NotNull(); |
147 | 0 | b.AddColumn("string_val")->Type(STRING)->NotNull(); |
148 | 0 | CHECK_OK(b.Build(&schema)); |
149 | 0 | std::unique_ptr<YBTableCreator> table_creator(client->NewTableCreator()); |
150 | 0 | return table_creator->table_name(table_name) |
151 | 0 | .schema(&schema) |
152 | 0 | .hash_schema(YBHashSchema::kMultiColumnHash) |
153 | 0 | .Create(); |
154 | 0 | } |
155 | | |
156 | | void VerifyTableExists(YBClient* client, |
157 | 0 | const YBTableName& table_name) { |
158 | 0 | LOG(INFO) << "Verifying that " << table_name.ToString() << " exists on leader.."; |
159 | 0 | const auto tables = ASSERT_RESULT(client->ListTables()); |
160 | 0 | ASSERT_TRUE(::util::gtl::contains(tables.begin(), tables.end(), table_name)); |
161 | 0 | } |
162 | | |
163 | 0 | void VerifyMasterRestart() { |
164 | 0 | LOG(INFO) << "Check that all " << num_masters_ << " masters are up first."; |
165 | 0 | for (int i = 0; i < num_masters_; ++i) { |
166 | 0 | LOG(INFO) << "Checking master " << i; |
167 | 0 | auto* master = cluster_->mini_master(i)->master(); |
168 | 0 | ASSERT_FALSE(master->IsShutdown()); |
169 | 0 | ASSERT_OK(master->WaitForCatalogManagerInit()); |
170 | 0 | } |
171 | |
|
172 | 0 | LOG(INFO) << "Restart the first master -- expected to succeed."; |
173 | 0 | auto* first_master = cluster_->mini_master(0); |
174 | 0 | ASSERT_OK(first_master->Restart()); |
175 | |
|
176 | 0 | LOG(INFO) << "Shutdown the master."; |
177 | 0 | first_master->Shutdown(); |
178 | |
|
179 | 0 | LOG(INFO) << "Normal start call should also work fine (and just reload the sys catalog)."; |
180 | 0 | ASSERT_OK(first_master->Start()); |
181 | 0 | } |
182 | | |
183 | | protected: |
184 | | int num_masters_; |
185 | | MiniClusterOptions opts_; |
186 | | }; |
187 | | |
188 | 0 | TEST_F(MasterReplicationTest, TestMasterClusterCreate) { |
189 | 0 | DontVerifyClusterBeforeNextTearDown(); |
190 | | |
191 | | // We want to confirm that the cluster starts properly and fails if you restart it. |
192 | 0 | ASSERT_NO_FATAL_FAILURE(VerifyMasterRestart()); |
193 | 0 | } |
194 | | |
195 | | // Basic test. Verify that: |
196 | | // |
197 | | // 1) We can start multiple masters in a distributed configuration and |
198 | | // that the clients and tablet servers can connect to the leader |
199 | | // master. |
200 | | // |
201 | | // 2) We can create a table (using the standard client APIs) on the |
202 | | // the leader and ensure that the appropriate table/tablet info is |
203 | | // replicated to the newly elected leader. |
204 | 0 | TEST_F(MasterReplicationTest, TestSysTablesReplication) { |
205 | | // Create the first table. |
206 | 0 | auto client = ASSERT_RESULT(CreateClient()); |
207 | 0 | ASSERT_OK(CreateTable(client.get(), kTableName1)); |
208 | | |
209 | | // TODO: once fault tolerant DDL is in, remove the line below. |
210 | 0 | client = ASSERT_RESULT(CreateClient()); |
211 | |
|
212 | 0 | ASSERT_OK(cluster_->WaitForTabletServerCount(kNumTabletServerReplicas)); |
213 | | |
214 | | // Repeat the same for the second table. |
215 | 0 | ASSERT_OK(CreateTable(client.get(), kTableName2)); |
216 | 0 | ASSERT_NO_FATALS(VerifyTableExists(client.get(), kTableName2)); |
217 | 0 | } |
218 | | |
219 | | // When all masters are down, test that we can timeout the connection |
220 | | // attempts after a specified deadline. |
221 | 0 | TEST_F(MasterReplicationTest, TestTimeoutWhenAllMastersAreDown) { |
222 | 0 | vector<string> master_addrs; |
223 | 0 | ListMasterServerAddrs(&master_addrs); |
224 | |
|
225 | 0 | cluster_->Shutdown(); |
226 | |
|
227 | 0 | YBClientBuilder builder; |
228 | 0 | builder.master_server_addrs(master_addrs); |
229 | 0 | builder.default_rpc_timeout(MonoDelta::FromMilliseconds(100)); |
230 | 0 | auto result = builder.Build(); |
231 | 0 | EXPECT_TRUE(!result.ok()); |
232 | 0 | EXPECT_TRUE(result.status().IsTimedOut()); |
233 | | |
234 | | // We need to reset 'cluster_' so that TearDown() can run correctly. |
235 | 0 | cluster_.reset(); |
236 | 0 | } |
237 | | |
238 | | // Shut the cluster down, start initializing the client, and then |
239 | | // bring the cluster back up during the initialization (but before the |
240 | | // timeout can elapse). |
241 | 0 | TEST_F(MasterReplicationTest, TestCycleThroughAllMasters) { |
242 | 0 | DontVerifyClusterBeforeNextTearDown(); |
243 | 0 | vector<string> master_addrs; |
244 | 0 | ListMasterServerAddrs(&master_addrs); |
245 | | |
246 | | // Shut the cluster down and ... |
247 | 0 | cluster_->Shutdown(); |
248 | | // ... start the cluster after a delay. |
249 | 0 | scoped_refptr<yb::Thread> start_thread; |
250 | 0 | ASSERT_OK(Thread::Create( |
251 | 0 | "TestCycleThroughAllMasters", "start_thread", |
252 | 0 | &MasterReplicationTest::StartClusterDelayed, |
253 | 0 | this, |
254 | 0 | 100 * 1000, // start after 100 millis. |
255 | 0 | &start_thread)); |
256 | | |
257 | | // Verify that the client doesn't give up even though the entire |
258 | | // cluster is down for 100 milliseconds. |
259 | 0 | YBClientBuilder builder; |
260 | 0 | builder.master_server_addrs(master_addrs); |
261 | | // Bumped up timeout from 15 sec to 30 sec because master election can take longer than 15 sec. |
262 | | // https://yugabyte.atlassian.net/browse/ENG-51 |
263 | | // Test log: https://gist.githubusercontent.com/mbautin/9f4269292e6ecb5b9a2fc644e2ee4398/raw |
264 | 0 | builder.default_admin_operation_timeout(MonoDelta::FromSeconds(30)); |
265 | 0 | EXPECT_OK(builder.Build()); |
266 | |
|
267 | 0 | ASSERT_OK(ThreadJoiner(start_thread.get()).Join()); |
268 | 0 | } |
269 | | |
270 | | } // namespace master |
271 | | } // namespace yb |