YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/integration-tests/master_path_handlers-itest.cc
Line
Count
Source (jump to first uncovered line)
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 <chrono>
15
16
#include "yb/client/schema.h"
17
#include "yb/client/table.h"
18
#include "yb/client/table_creator.h"
19
#include "yb/client/yb_table_name.h"
20
21
#include "yb/common/partition.h"
22
23
#include "yb/integration-tests/external_mini_cluster.h"
24
#include "yb/integration-tests/mini_cluster.h"
25
#include "yb/integration-tests/yb_mini_cluster_test_base.h"
26
27
#include "yb/master/master-path-handlers.h"
28
#include "yb/master/mini_master.h"
29
30
#include "yb/tools/yb-admin_client.h"
31
32
#include "yb/tserver/mini_tablet_server.h"
33
34
#include "yb/util/curl_util.h"
35
#include "yb/util/jsonreader.h"
36
#include "yb/util/random_util.h"
37
#include "yb/util/result.h"
38
#include "yb/util/test_macros.h"
39
40
DECLARE_int32(tserver_unresponsive_timeout_ms);
41
DECLARE_int32(heartbeat_interval_ms);
42
DECLARE_string(TEST_master_extra_list_host_port);
43
44
DECLARE_int32(follower_unavailable_considered_failed_sec);
45
46
namespace yb {
47
namespace master {
48
49
using std::string;
50
51
const std::string kKeyspaceName("my_keyspace");
52
const uint kNumMasters(3);
53
const uint kNumTablets(3);
54
55
template <class T>
56
class MasterPathHandlersBaseItest : public YBMiniClusterTestBase<T> {
57
 public:
58
3
  void DoTearDown() override {
59
3
    cluster_->Shutdown();
60
3
  }
_ZN2yb6master27MasterPathHandlersBaseItestINS_11MiniClusterEE10DoTearDownEv
Line
Count
Source
58
2
  void DoTearDown() override {
59
2
    cluster_->Shutdown();
60
2
  }
_ZN2yb6master27MasterPathHandlersBaseItestINS_19ExternalMiniClusterEE10DoTearDownEv
Line
Count
Source
58
1
  void DoTearDown() override {
59
1
    cluster_->Shutdown();
60
1
  }
61
62
 protected:
63
1
  void TestUrl(const string& query_path, faststring* result) {
64
1
    const string tables_url = master_http_url_ + query_path;
65
1
    EasyCurl curl;
66
1
    ASSERT_OK(curl.FetchURL(tables_url, result));
67
1
  }
Unexecuted instantiation: _ZN2yb6master27MasterPathHandlersBaseItestINS_11MiniClusterEE7TestUrlERKNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEPNS_10faststringE
_ZN2yb6master27MasterPathHandlersBaseItestINS_19ExternalMiniClusterEE7TestUrlERKNSt3__112basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEPNS_10faststringE
Line
Count
Source
63
1
  void TestUrl(const string& query_path, faststring* result) {
64
1
    const string tables_url = master_http_url_ + query_path;
65
1
    EasyCurl curl;
66
1
    ASSERT_OK(curl.FetchURL(tables_url, result));
67
1
  }
68
69
5
  virtual int num_masters() const {
70
5
    return kNumMasters;
71
5
  }
_ZNK2yb6master27MasterPathHandlersBaseItestINS_11MiniClusterEE11num_mastersEv
Line
Count
Source
69
4
  virtual int num_masters() const {
70
4
    return kNumMasters;
71
4
  }
_ZNK2yb6master27MasterPathHandlersBaseItestINS_19ExternalMiniClusterEE11num_mastersEv
Line
Count
Source
69
1
  virtual int num_masters() const {
70
1
    return kNumMasters;
71
1
  }
72
73
  using YBMiniClusterTestBase<T>::cluster_;
74
  string master_http_url_;
75
};
76
77
class MasterPathHandlersItest : public MasterPathHandlersBaseItest<MiniCluster> {
78
 public:
79
5
  void SetUp() override {
80
5
    YBMiniClusterTestBase::SetUp();
81
5
    MiniClusterOptions opts;
82
    // Set low heartbeat timeout.
83
5
    FLAGS_tserver_unresponsive_timeout_ms = 5000;
84
5
    opts.num_tablet_servers = kNumTablets;
85
5
    opts.num_masters = num_masters();
86
5
    cluster_.reset(new MiniCluster(opts));
87
5
    ASSERT_OK(cluster_->Start());
88
89
0
    Endpoint master_http_endpoint =
90
0
        ASSERT_RESULT(cluster_->GetLeaderMiniMaster())->bound_http_addr();
91
0
    master_http_url_ = "http://" + AsString(master_http_endpoint);
92
0
  }
93
};
94
95
0
bool verifyTServersAlive(int n, const string& result) {
96
0
  size_t pos = 0;
97
0
  for (int i = 0; i < n; i++) {
98
0
    pos = result.find(kTserverAlive, pos + 1);
99
0
    if (pos == string::npos) {
100
0
      return false;
101
0
    }
102
0
  }
103
0
  return result.find(kTserverAlive, pos + 1) == string::npos;
104
0
}
105
106
0
TEST_F(MasterPathHandlersItest, TestMasterPathHandlers) {
107
0
  faststring result;
108
0
  TestUrl("/table?id=1", &result);
109
0
  TestUrl("/tablet-servers", &result);
110
0
  TestUrl("/tables", &result);
111
0
  TestUrl("/dump-entities", &result);
112
0
  TestUrl("/cluster-config", &result);
113
0
  TestUrl("/tablet-replication", &result);
114
0
}
115
116
0
TEST_F(MasterPathHandlersItest, TestDeadTServers) {
117
  // Shutdown tserver and wait for heartbeat timeout.
118
0
  cluster_->mini_tablet_server(0)->Shutdown();
119
0
  std::this_thread::sleep_for(std::chrono::milliseconds(2 * FLAGS_tserver_unresponsive_timeout_ms));
120
121
  // Check UI page.
122
0
  faststring result;
123
0
  TestUrl("/tablet-servers", &result);
124
0
  const string &result_str = result.ToString();
125
0
  ASSERT_TRUE(verifyTServersAlive(2, result_str));
126
127
  // Now verify dead.
128
0
  size_t pos = result_str.find(kTserverDead, 0);
129
0
  ASSERT_TRUE(pos != string::npos);
130
0
  ASSERT_TRUE(result_str.find(kTserverDead, pos + 1) == string::npos);
131
132
  // Startup the tserver and wait for heartbeats.
133
0
  ASSERT_OK(cluster_->mini_tablet_server(0)->Start());
134
135
0
  ASSERT_OK(WaitFor([&]() -> bool {
136
0
    TestUrl("/tablet-servers", &result);
137
0
    return verifyTServersAlive(3, result.ToString());
138
0
  }, MonoDelta::FromSeconds(10), "Waiting for tserver heartbeat to master"));
139
0
}
140
141
0
TEST_F(MasterPathHandlersItest, TestTabletReplicationEndpoint) {
142
0
  auto client = ASSERT_RESULT(cluster_->CreateClient());
143
0
  ASSERT_OK(client->CreateNamespaceIfNotExists(kKeyspaceName));
144
145
  // Create table.
146
  // TODO(5016): Consolidate into some standardized helper code.
147
0
  client::YBTableName table_name(YQL_DATABASE_CQL, kKeyspaceName, "test_table");
148
0
  client::YBSchema schema;
149
0
  client::YBSchemaBuilder b;
150
0
  b.AddColumn("key")->Type(INT32)->NotNull()->PrimaryKey();
151
0
  b.AddColumn("int_val")->Type(INT32)->NotNull();
152
0
  b.AddColumn("string_val")->Type(STRING)->NotNull();
153
0
  ASSERT_OK(b.Build(&schema));
154
0
  std::unique_ptr<client::YBTableCreator> table_creator(client->NewTableCreator());
155
0
  ASSERT_OK(table_creator->table_name(table_name)
156
0
      .schema(&schema)
157
0
      .hash_schema(YBHashSchema::kMultiColumnHash)
158
0
      .Create());
159
0
  std::shared_ptr<client::YBTable> table;
160
0
  ASSERT_OK(client->OpenTable(table_name, &table));
161
162
  // Choose a tablet to orphan and take note of the servers which are leaders/followers for this
163
  // tablet.
164
0
  google::protobuf::RepeatedPtrField<TabletLocationsPB> tablets;
165
0
  ASSERT_OK(client->GetTabletsFromTableId(table->id(), kNumTablets, &tablets));
166
0
  std::vector<yb::tserver::MiniTabletServer *> followers;
167
0
  yb::tserver::MiniTabletServer* leader = nullptr;
168
0
  auto orphan_tablet = tablets.Get(0);
169
0
  for (const auto& replica : orphan_tablet.replicas()) {
170
0
    const auto uuid = replica.ts_info().permanent_uuid();
171
0
    auto* tserver = cluster_->find_tablet_server(uuid);
172
0
    ASSERT_NOTNULL(tserver);
173
0
    if (replica.role() == PeerRole::LEADER) {
174
0
      leader = tserver;
175
0
    } else {
176
0
      followers.push_back(tserver);
177
0
    }
178
    // Shutdown all tservers.
179
0
    tserver->Shutdown();
180
0
  }
181
0
  ASSERT_NOTNULL(leader);
182
183
  // Restart the server which was previously the leader of the now orphaned tablet.
184
0
  ASSERT_OK(leader->Start());
185
  // Sleep here to give the master's catalog_manager time to receive heartbeat from "leader".
186
0
  std::this_thread::sleep_for(std::chrono::milliseconds(6 * FLAGS_heartbeat_interval_ms));
187
188
  // Call endpoint and validate format of response.
189
0
  faststring result;
190
0
  TestUrl("/api/v1/tablet-replication", &result);
191
192
0
  JsonReader r(result.ToString());
193
0
  ASSERT_OK(r.Init());
194
0
  const rapidjson::Value* json_obj = nullptr;
195
0
  EXPECT_OK(r.ExtractObject(r.root(), NULL, &json_obj));
196
0
  EXPECT_EQ(rapidjson::kObjectType, CHECK_NOTNULL(json_obj)->GetType());
197
0
  EXPECT_TRUE(json_obj->HasMember("leaderless_tablets"));
198
0
  EXPECT_EQ(rapidjson::kArrayType, (*json_obj)["leaderless_tablets"].GetType());
199
0
  const rapidjson::Value::ConstArray tablets_json = (*json_obj)["leaderless_tablets"].GetArray();
200
0
  std::vector<std::string> leaderless_tablet_uuids;
201
0
  for (const auto& tablet_json : tablets_json) {
202
0
    EXPECT_EQ(rapidjson::kObjectType, tablet_json.GetType());
203
0
    EXPECT_TRUE(tablet_json.HasMember("table_uuid"));
204
0
    EXPECT_EQ(rapidjson::kStringType, tablet_json["table_uuid"].GetType());
205
0
    EXPECT_TRUE(tablet_json.HasMember("tablet_uuid"));
206
0
    EXPECT_EQ(rapidjson::kStringType, tablet_json["tablet_uuid"].GetType());
207
0
  }
208
209
0
  auto has_orphan_tablet_result = std::any_of(
210
0
      tablets_json.begin(), tablets_json.end(),
211
0
      [&orphan_tablet](const auto& tablet_json) {
212
0
        return tablet_json["tablet_uuid"].GetString() == orphan_tablet.tablet_id();
213
0
      });
214
0
  EXPECT_TRUE(has_orphan_tablet_result) << "Expected to find orphan_tablet in leaderless tablets.";
215
216
  // YBMiniClusterTestBase test-end verification will fail if the cluster is up with stopped nodes.
217
0
  cluster_->Shutdown();
218
0
}
219
220
0
TEST_F(MasterPathHandlersItest, TestTabletUnderReplicationEndpoint) {
221
  // Set test specific flag
222
0
  FLAGS_follower_unavailable_considered_failed_sec = 30;
223
224
0
  auto client = ASSERT_RESULT(cluster_->CreateClient());
225
0
  ASSERT_OK(client->CreateNamespaceIfNotExists(kKeyspaceName));
226
227
  // Create table.
228
0
  client::YBTableName table_name(YQL_DATABASE_CQL, kKeyspaceName, "test_table");
229
0
  client::YBSchema schema;
230
0
  client::YBSchemaBuilder b;
231
0
  b.AddColumn("key")->Type(INT32)->NotNull()->PrimaryKey();
232
0
  b.AddColumn("int_val")->Type(INT32)->NotNull();
233
0
  b.AddColumn("string_val")->Type(STRING)->NotNull();
234
0
  ASSERT_OK(b.Build(&schema));
235
0
  std::unique_ptr<client::YBTableCreator> table_creator(client->NewTableCreator());
236
0
  ASSERT_OK(table_creator->table_name(table_name)
237
0
      .schema(&schema)
238
0
      .hash_schema(YBHashSchema::kMultiColumnHash)
239
0
      .Create());
240
0
  std::shared_ptr<client::YBTable> table;
241
0
  ASSERT_OK(client->OpenTable(table_name, &table));
242
243
  // Get all the tablets of this table and store them
244
0
  google::protobuf::RepeatedPtrField<TabletLocationsPB> tablets;
245
0
  ASSERT_OK(client->GetTabletsFromTableId(table->id(), kNumTablets, &tablets));
246
247
0
  std::vector<std::string> tIds;
248
0
  bool isTestTrue = true;
249
250
0
  int numTablets = tablets.size();
251
0
  for(int i = 0; i < numTablets; i++) {
252
0
    auto tablet = tablets.Get(i);
253
0
    tIds.push_back(tablet.tablet_id());
254
0
  }
255
256
  // Now kill one of the servers
257
  // Since the replication factor is 3 and the number
258
  // of nodes is also 3, all the tablets
259
  // of this table should become under replicated
260
0
  cluster_->mini_tablet_server(0)->Shutdown();
261
  // Wait for 3*30 secs just to be safe
262
0
  std::this_thread::sleep_for(std::chrono::milliseconds(
263
0
    3 * FLAGS_follower_unavailable_considered_failed_sec * 1000));
264
265
  // Call endpoint and validate format of response.
266
0
  faststring result;
267
0
  TestUrl("/api/v1/tablet-under-replication", &result);
268
269
0
  JsonReader r(result.ToString());
270
0
  ASSERT_OK(r.Init());
271
0
  const rapidjson::Value* json_obj = nullptr;
272
0
  EXPECT_OK(r.ExtractObject(r.root(), NULL, &json_obj));
273
0
  EXPECT_EQ(rapidjson::kObjectType, CHECK_NOTNULL(json_obj)->GetType());
274
0
  EXPECT_TRUE(json_obj->HasMember("underreplicated_tablets"));
275
0
  EXPECT_EQ(rapidjson::kArrayType, (*json_obj)["underreplicated_tablets"].GetType());
276
0
  const rapidjson::Value::ConstArray tablets_json =
277
0
      (*json_obj)["underreplicated_tablets"].GetArray();
278
279
0
  for (const auto& tablet_json : tablets_json) {
280
0
    EXPECT_EQ(rapidjson::kObjectType, tablet_json.GetType());
281
0
    EXPECT_TRUE(tablet_json.HasMember("table_uuid"));
282
0
    EXPECT_EQ(rapidjson::kStringType, tablet_json["table_uuid"].GetType());
283
0
    EXPECT_TRUE(tablet_json.HasMember("tablet_uuid"));
284
0
    EXPECT_EQ(rapidjson::kStringType, tablet_json["tablet_uuid"].GetType());
285
0
  }
286
287
  // These tablets should be present in the json response
288
0
  for(const std::string &id : tIds) {
289
0
    isTestTrue = isTestTrue && std::any_of(tablets_json.begin(), tablets_json.end(),
290
0
                      [&id](const auto &tablet_json) {
291
0
                          return tablet_json["tablet_uuid"].GetString() == id;
292
0
                      });
293
0
  }
294
295
0
  EXPECT_TRUE(isTestTrue) << "Expected to find under-replicated tablets.";
296
297
  // YBMiniClusterTestBase test-end verification will fail if the cluster is up with stopped nodes.
298
0
  cluster_->Shutdown();
299
0
}
300
301
class MultiMasterPathHandlersItest : public MasterPathHandlersItest {
302
 public:
303
1
  int num_masters() const override {
304
1
    return 3;
305
1
  }
306
};
307
308
0
TEST_F_EX(MasterPathHandlersItest, Forward, MultiMasterPathHandlersItest) {
309
0
  FLAGS_TEST_master_extra_list_host_port = RandomHumanReadableString(16) + ".com";
310
0
  EasyCurl curl;
311
0
  faststring content;
312
0
  for (size_t i = 0; i != cluster_->num_masters(); ++i) {
313
0
    auto url = Format("http://$0/tablet-servers", cluster_->mini_master(i)->bound_http_addr());
314
0
    content.clear();
315
0
    ASSERT_OK(curl.FetchURL(url, &content));
316
0
  }
317
0
}
318
319
class MasterPathHandlersExternalItest : public MasterPathHandlersBaseItest<ExternalMiniCluster> {
320
 public:
321
1
  void SetUp() override {
322
1
    YBMiniClusterTestBase::SetUp();
323
1
    ExternalMiniClusterOptions opts;
324
    // Set low heartbeat timeout.
325
1
    FLAGS_tserver_unresponsive_timeout_ms = 5000;
326
1
    opts.num_tablet_servers = kNumTablets;
327
1
    opts.num_masters = num_masters();
328
1
    cluster_.reset(new ExternalMiniCluster(opts));
329
1
    ASSERT_OK(cluster_->Start());
330
331
1
    HostPort master_http_endpoint = cluster_->master(0)->bound_http_hostport();
332
1
    master_http_url_ = "http://" + ToString(master_http_endpoint);
333
1
  }
334
};
335
336
1
TEST_F_EX(MasterPathHandlersItest, TestTablePlacementInfo, MasterPathHandlersExternalItest) {
337
1
  auto client = ASSERT_RESULT(cluster_->CreateClient());
338
1
  ASSERT_OK(client->CreateNamespaceIfNotExists(kKeyspaceName));
339
340
  // Create table.
341
  // TODO(5016): Consolidate into some standardized helper code.
342
1
  client::YBTableName table_name(YQL_DATABASE_CQL, kKeyspaceName, "test_table");
343
1
  client::YBSchema schema;
344
1
  client::YBSchemaBuilder b;
345
1
  b.AddColumn("key")->Type(INT32)->NotNull()->PrimaryKey();
346
1
  b.AddColumn("int_val")->Type(INT32)->NotNull();
347
1
  b.AddColumn("string_val")->Type(STRING)->NotNull();
348
1
  ASSERT_OK(b.Build(&schema));
349
1
  std::unique_ptr<client::YBTableCreator> table_creator(client->NewTableCreator());
350
1
  ASSERT_OK(table_creator->table_name(table_name)
351
1
      .schema(&schema)
352
1
      .hash_schema(YBHashSchema::kMultiColumnHash)
353
1
      .Create());
354
1
  std::shared_ptr<client::YBTable> table;
355
1
  ASSERT_OK(client->OpenTable(table_name, &table));
356
357
  // Verify replication info is empty.
358
1
  faststring result;
359
1
  auto url = Format("/table?id=$0", table->id());
360
1
  TestUrl(url, &result);
361
1
  const string& result_str = result.ToString();
362
1
  size_t pos = result_str.find("Replication Info", 0);
363
1
  ASSERT_NE(pos, string::npos);
364
1
  ASSERT_EQ(result_str.find("live_replicas", pos + 1), string::npos);
365
366
  // Verify cluster level replication info.
367
1
  auto yb_admin_client_ = std::make_unique<yb::tools::enterprise::ClusterAdminClient>(
368
1
    cluster_->GetMasterAddresses(), MonoDelta::FromSeconds(30));
369
1
  ASSERT_OK(yb_admin_client_->Init());
370
1
  ASSERT_OK(yb_admin_client_->ModifyPlacementInfo("cloud.region.zone", 3, "table_uuid"));
371
0
  TestUrl(url, &result);
372
0
  const string& cluster_str = result.ToString();
373
0
  pos = cluster_str.find("Replication Info", 0);
374
0
  ASSERT_NE(pos, string::npos);
375
0
  pos = cluster_str.find("placement_zone", pos + 1);
376
0
  ASSERT_NE(pos, string::npos);
377
0
  ASSERT_EQ(cluster_str.substr(pos + 17, 4), "zone");
378
379
  // Verify table level replication info.
380
0
  ASSERT_OK(yb_admin_client_->ModifyTablePlacementInfo(
381
0
    table->name(), "cloud.region.anotherzone", 3, "table_uuid"));
382
0
  TestUrl(url, &result);
383
0
  const string& table_str = result.ToString();
384
0
  pos = table_str.find("Replication Info", 0);
385
0
  ASSERT_NE(pos, string::npos);
386
0
  pos = table_str.find("placement_zone", pos + 1);
387
0
  ASSERT_NE(pos, string::npos);
388
0
  ASSERT_EQ(table_str.substr(pos + 17, 11), "anotherzone");
389
0
}
390
391
} // namespace master
392
} // namespace yb