YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/tools/ysck.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 "yb/tools/ysck.h"
34
35
#include <mutex>
36
#include <unordered_set>
37
38
#include <glog/logging.h>
39
40
#include "yb/gutil/bind.h"
41
#include "yb/gutil/map-util.h"
42
#include "yb/gutil/ref_counted.h"
43
#include "yb/gutil/strings/join.h"
44
#include "yb/gutil/strings/substitute.h"
45
46
#include "yb/util/blocking_queue.h"
47
#include "yb/util/countdown_latch.h"
48
#include "yb/util/locks.h"
49
#include "yb/util/monotime.h"
50
51
namespace yb {
52
namespace tools {
53
54
using std::ostream;
55
using std::shared_ptr;
56
using std::string;
57
using std::unordered_map;
58
using strings::Substitute;
59
60
DEFINE_int32(checksum_timeout_sec, 120,
61
             "Maximum total seconds to wait for a checksum scan to complete "
62
             "before timing out.");
63
DEFINE_int32(checksum_scan_concurrency, 4,
64
             "Number of concurrent checksum scans to execute per tablet server.");
65
66
ChecksumOptions::ChecksumOptions()
67
    : timeout(MonoDelta::FromSeconds(FLAGS_checksum_timeout_sec)),
68
35
      scan_concurrency(FLAGS_checksum_scan_concurrency) {}
69
70
ChecksumOptions::ChecksumOptions(MonoDelta timeout, int scan_concurrency)
71
    : timeout(std::move(timeout)),
72
0
      scan_concurrency(scan_concurrency) {}
73
74
0
string YsckTable::ToString() const {
75
0
  return Format(
76
0
      "id: $0 name: $1 schema: $2 num_replicas: $3 table_type: $4",
77
0
      id_,
78
0
      name_,
79
0
      schema_,
80
0
      num_replicas_,
81
0
      yb::TableType_Name(table_type_));
82
0
}
83
84
1.23k
YsckCluster::~YsckCluster() {
85
1.23k
}
86
87
1.23k
Status YsckCluster::FetchTableAndTabletInfo() {
88
1.23k
  RETURN_NOT_OK(master_->Connect());
89
1.23k
  RETURN_NOT_OK(RetrieveTablesList());
90
962
  RETURN_NOT_OK(RetrieveTabletServers());
91
17.3k
  for (const shared_ptr<YsckTable>& table : tables()) {
92
17.3k
    RETURN_NOT_OK(RetrieveTabletsList(table));
93
17.3k
  }
94
956
  return Status::OK();
95
962
}
96
97
// Gets the list of tablet servers from the Master.
98
962
Status YsckCluster::RetrieveTabletServers() {
99
962
  return master_->RetrieveTabletServers(&tablet_servers_);
100
962
}
101
102
// Gets the list of tables from the Master.
103
1.23k
Status YsckCluster::RetrieveTablesList() {
104
1.23k
  return master_->RetrieveTablesList(&tables_);
105
1.23k
}
106
107
17.3k
Status YsckCluster::RetrieveTabletsList(const shared_ptr<YsckTable>& table) {
108
17.3k
  return master_->RetrieveTabletsList(table);
109
17.3k
}
110
111
1.23k
Status Ysck::CheckMasterRunning() {
112
0
  VLOG(1) << "Connecting to the Master";
113
1.23k
  Status s = cluster_->master()->Connect();
114
1.23k
  if (s.ok()) {
115
1.23k
    LOG(INFO) << "Connected to the Master";
116
1.23k
  }
117
1.23k
  return s;
118
1.23k
}
119
120
1.23k
Status Ysck::FetchTableAndTabletInfo() {
121
1.23k
  return cluster_->FetchTableAndTabletInfo();
122
1.23k
}
123
124
956
Status Ysck::CheckTabletServersRunning() {
125
0
  VLOG(1) << "Getting the Tablet Servers list";
126
956
  auto servers_count = cluster_->tablet_servers().size();
127
0
  VLOG(1) << Substitute("List of $0 Tablet Servers retrieved", servers_count);
128
129
956
  if (servers_count == 0) {
130
0
    return STATUS(NotFound, "No tablet servers found");
131
0
  }
132
133
956
  size_t bad_servers = 0;
134
0
  VLOG(1) << "Connecting to all the Tablet Servers";
135
2.84k
  for (const YsckMaster::TSMap::value_type& entry : cluster_->tablet_servers()) {
136
2.84k
    Status s = ConnectToTabletServer(entry.second);
137
2.84k
    if (!s.ok()) {
138
650
      bad_servers++;
139
650
    }
140
2.84k
  }
141
956
  if (bad_servers == 0) {
142
636
    LOG(INFO) << Substitute("Connected to all $0 Tablet Servers", servers_count);
143
636
    return Status::OK();
144
320
  } else {
145
320
    LOG(WARNING) << Substitute("Connected to $0 Tablet Servers, $1 weren't reachable",
146
320
                               servers_count - bad_servers, bad_servers);
147
320
    return STATUS(NetworkError, "Not all Tablet Servers are reachable");
148
320
  }
149
956
}
150
151
2.84k
Status Ysck::ConnectToTabletServer(const shared_ptr<YsckTabletServer>& ts) {
152
0
  VLOG(1) << "Going to connect to Tablet Server: " << ts->uuid();
153
2.84k
  Status s = ts->Connect();
154
2.84k
  if (s.ok()) {
155
0
    VLOG(1) << "Connected to Tablet Server: " << ts->uuid();
156
650
  } else {
157
650
    LOG(WARNING) << Substitute("Unable to connect to Tablet Server $0 because $1",
158
650
                               ts->uuid(), s.ToString());
159
650
  }
160
2.84k
  return s;
161
2.84k
}
162
163
636
Status Ysck::CheckTablesConsistency() {
164
0
  VLOG(1) << "Getting the tables list";
165
636
  auto tables_count = cluster_->tables().size();
166
0
  VLOG(1) << Substitute("List of $0 tables retrieved", tables_count);
167
168
636
  if (tables_count == 0) {
169
0
    LOG(INFO) << "The cluster doesn't have any tables";
170
0
    return Status::OK();
171
0
  }
172
173
0
  VLOG(1) << "Verifying each table";
174
636
  size_t bad_tables_count = 0;
175
11.5k
  for (const shared_ptr<YsckTable> &table : cluster_->tables()) {
176
11.5k
    if (!VerifyTable(table)) {
177
8
      bad_tables_count++;
178
8
    }
179
11.5k
  }
180
636
  if (bad_tables_count == 0) {
181
628
    LOG(INFO) << Substitute("The metadata for $0 tables is HEALTHY", tables_count);
182
628
    return Status::OK();
183
8
  } else {
184
8
    LOG(WARNING) << Substitute("$0 out of $1 tables are not in a healthy state",
185
8
                               bad_tables_count, tables_count);
186
8
    return STATUS(Corruption, Substitute("$0 tables are bad", bad_tables_count));
187
8
  }
188
636
}
189
190
// Class to act as a collector of scan results.
191
// Provides thread-safe accessors to update and read a hash table of results.
192
class ChecksumResultReporter : public RefCountedThreadSafe<ChecksumResultReporter> {
193
 public:
194
  typedef std::pair<Status, uint64_t> ResultPair;
195
  typedef std::vector<ResultPair> TableResults;
196
  typedef std::unordered_map<std::string, TableResults> ReplicaResultMap;
197
  typedef std::unordered_map<std::string, ReplicaResultMap> TabletResultMap;
198
199
  // Initialize reporter with the number of replicas being queried.
200
  explicit ChecksumResultReporter(int num_tablet_replicas)
201
628
      : responses_(num_tablet_replicas) {
202
628
  }
203
204
  // Write an entry to the result map indicating a response from the remote.
205
  void ReportResult(const std::string& tablet_id,
206
                    const std::string& replica_uuid,
207
                    const Status& status,
208
4.19k
                    uint64_t checksum) {
209
4.19k
    std::lock_guard<simple_spinlock> guard(lock_);
210
4.19k
    unordered_map<string, TableResults>& replica_results =
211
4.19k
        LookupOrInsert(&checksums_, tablet_id, unordered_map<string, TableResults>());
212
4.19k
    if (replica_results.find(replica_uuid) == replica_results.end()) {
213
4.19k
      TableResults table_results(1, ResultPair(status, checksum));
214
4.19k
      replica_results[replica_uuid] = table_results;
215
18.4E
    } else {
216
18.4E
      replica_results[replica_uuid].push_back(ResultPair(status, checksum));
217
18.4E
    }
218
4.19k
    responses_.CountDown();
219
4.19k
  }
220
221
  // Blocks until either the number of results plus errors reported equals
222
  // num_tablet_replicas (from the constructor), or until the timeout expires,
223
  // whichever comes first.
224
  // Returns false if the timeout expired before all responses came in.
225
  // Otherwise, returns true.
226
628
  bool WaitFor(const MonoDelta& timeout) const { return responses_.WaitFor(timeout); }
227
228
  // Returns true iff all replicas have reported in.
229
0
  bool AllReported() const { return responses_.count() == 0; }
230
231
  // Get reported results.
232
628
  TabletResultMap checksums() const {
233
628
    std::lock_guard<simple_spinlock> guard(lock_);
234
628
    return checksums_;
235
628
  }
236
237
 private:
238
  friend class RefCountedThreadSafe<ChecksumResultReporter>;
239
628
  ~ChecksumResultReporter() {}
240
241
  // Report either a success or error response.
242
  void HandleResponse(const std::string& tablet_id, const std::string& replica_uuid,
243
                      const Status& status, uint64_t checksum);
244
245
  CountDownLatch responses_;
246
  mutable simple_spinlock lock_; // Protects 'checksums_'.
247
  // checksums_ is an unordered_map of { tablet_id : { replica_uuid : checksum } }.
248
  TabletResultMap checksums_;
249
};
250
251
// Queue of tablet replicas for an individual tablet server.
252
typedef shared_ptr<BlockingQueue<std::pair<Schema, std::string> > > TabletQueue;
253
254
// A callback function which records the result of a tablet replica's checksum,
255
// and then checks if the tablet server has any more tablets to checksum. If so,
256
// a new async checksum scan is started.
257
void TabletServerChecksumCallback(
258
    const scoped_refptr<ChecksumResultReporter>& reporter,
259
    const shared_ptr<YsckTabletServer>& tablet_server,
260
    const TabletQueue& queue,
261
    const TabletId& tablet_id,
262
    const ChecksumOptions& options,
263
    const Status& status,
264
4.19k
    uint64_t checksum) {
265
4.19k
  reporter->ReportResult(tablet_id, tablet_server->uuid(), status, checksum);
266
267
4.19k
  std::pair<Schema, TabletId> table_tablet;
268
4.19k
  if (queue->BlockingGet(&table_tablet)) {
269
20
    const Schema& table_schema = table_tablet.first;
270
20
    const TabletId& tablet_id = table_tablet.second;
271
20
    ReportResultCallback callback = Bind(&TabletServerChecksumCallback,
272
20
                                         reporter,
273
20
                                         tablet_server,
274
20
                                         queue,
275
20
                                         tablet_id,
276
20
                                         options);
277
20
    tablet_server->RunTabletChecksumScanAsync(tablet_id, table_schema, options, callback);
278
20
  }
279
4.19k
}
280
281
Status Ysck::ChecksumData(const vector<string>& tables,
282
                          const vector<string>& tablets,
283
628
                          const ChecksumOptions& opts) {
284
628
  const std::unordered_set<std::string> tables_filter(tables.begin(), tables.end());
285
628
  const std::unordered_set<std::string> tablets_filter(tablets.begin(), tablets.end());
286
287
  // Copy options so that local modifications can be made and passed on.
288
628
  ChecksumOptions options = opts;
289
290
628
  using TabletTableMap = std::unordered_map<
291
628
      std::shared_ptr<YsckTablet>, std::vector<std::shared_ptr<YsckTable>>>;
292
628
  TabletTableMap tablet_table_map;
293
294
628
  int num_tablet_replicas = 0;
295
628
  bool there_are_non_system_tables = false;
296
11.4k
  for (const shared_ptr<YsckTable>& table : cluster_->tables()) {
297
11.4k
    if (table->name().is_system()) {
298
      // Skip the system namespace with virtual tables, since they are not assigned to tservers.
299
10.7k
      continue;
300
10.7k
    }
301
302
    // TODO: remove once we have is_system() implemented correctly for PostgreSQL tables.
303
628
    if (table->table_type() == PGSQL_TABLE_TYPE)
304
0
      continue;
305
306
628
    there_are_non_system_tables = true;
307
0
    VLOG(1) << "Table: " << table->name().ToString();
308
628
    if (!tables_filter.empty() && !ContainsKey(tables_filter, table->name().table_name())) continue;
309
    // TODO: remove once we have scan implemented for Redis.
310
628
    if (table->table_type() == REDIS_TABLE_TYPE) continue;
311
1.41k
    for (const shared_ptr<YsckTablet>& tablet : table->tablets()) {
312
0
      VLOG(1) << "Tablet: " << tablet->id();
313
1.41k
      if (!tablets_filter.empty() && !ContainsKey(tablets_filter, tablet->id())) continue;
314
1.41k
      if (tablet_table_map.find(tablet) == tablet_table_map.end()) {
315
1.41k
        tablet_table_map[tablet] = std::vector<shared_ptr<YsckTable>>(1, table);
316
0
      } else {
317
0
        tablet_table_map[tablet].push_back(table);
318
0
      }
319
1.41k
      num_tablet_replicas += tablet->replicas().size();
320
1.41k
    }
321
628
  }
322
  // Number of tablet replicas can be zero if there are no user tables available.
323
628
  if (there_are_non_system_tables && num_tablet_replicas == 0) {
324
0
    string msg = "No tablet replicas found.";
325
0
    if (!tables.empty() || !tablets.empty()) {
326
0
      msg += " Filter: ";
327
0
      if (!tables.empty()) {
328
0
        msg += "tables=" + JoinStrings(tables, ",") + ".";
329
0
      }
330
0
      if (!tablets.empty()) {
331
0
        msg += "tablets=" + JoinStrings(tablets, ",") + ".";
332
0
      }
333
0
    }
334
0
    return STATUS(NotFound, msg);
335
0
  }
336
337
  // Map of tablet servers to tablet queue.
338
628
  typedef unordered_map<shared_ptr<YsckTabletServer>, TabletQueue> TabletServerQueueMap;
339
340
628
  TabletServerQueueMap tablet_server_queues;
341
628
  scoped_refptr<ChecksumResultReporter> reporter(new ChecksumResultReporter(num_tablet_replicas));
342
343
  // Create a queue of checksum callbacks grouped by the tablet server.
344
1.41k
  for (const TabletTableMap::value_type& entry : tablet_table_map) {
345
1.41k
    const shared_ptr<YsckTablet>& tablet = entry.first;
346
1.41k
    for (const shared_ptr<YsckTable>& table : entry.second) {
347
4.19k
      for (const shared_ptr<YsckTabletReplica>& replica : tablet->replicas()) {
348
4.19k
        const shared_ptr<YsckTabletServer>& ts =
349
4.19k
            FindOrDie(cluster_->tablet_servers(), replica->ts_uuid());
350
351
4.19k
        const TabletQueue& queue =
352
4.19k
            LookupOrInsertNewSharedPtr(&tablet_server_queues, ts, num_tablet_replicas);
353
4.19k
        CHECK_EQ(QUEUE_SUCCESS, queue->Put(make_pair(table->schema(), tablet->id())));
354
4.19k
      }
355
1.41k
    }
356
1.41k
  }
357
358
  // Kick off checksum scans in parallel. For each tablet server, we start
359
  // scan_concurrency scans. Each callback then initiates one additional
360
  // scan when it returns if the queue for that TS is not empty.
361
1.87k
  for (const TabletServerQueueMap::value_type& entry : tablet_server_queues) {
362
1.87k
    const shared_ptr<YsckTabletServer>& tablet_server = entry.first;
363
1.87k
    const TabletQueue& queue = entry.second;
364
1.87k
    queue->Shutdown(); // Ensures that BlockingGet() will not block.
365
9.37k
    for (int i = 0; i < options.scan_concurrency; i++) {
366
7.50k
      std::pair<Schema, std::string> table_tablet;
367
7.50k
      if (queue->BlockingGet(&table_tablet)) {
368
4.17k
        const Schema& table_schema = table_tablet.first;
369
4.17k
        const std::string& tablet_id = table_tablet.second;
370
4.17k
        ReportResultCallback callback = Bind(&TabletServerChecksumCallback,
371
4.17k
                                             reporter,
372
4.17k
                                             tablet_server,
373
4.17k
                                             queue,
374
4.17k
                                             tablet_id,
375
4.17k
                                             options);
376
4.17k
        tablet_server->RunTabletChecksumScanAsync(tablet_id, table_schema, options, callback);
377
4.17k
      }
378
7.50k
    }
379
1.87k
  }
380
381
628
  bool timed_out = false;
382
628
  if (!reporter->WaitFor(options.timeout)) {
383
0
    timed_out = true;
384
0
  }
385
628
  ChecksumResultReporter::TabletResultMap checksums = reporter->checksums();
386
387
628
  int num_errors = 0;
388
628
  int num_mismatches = 0;
389
628
  int num_results = 0;
390
11.4k
  for (const shared_ptr<YsckTable>& table : cluster_->tables()) {
391
11.4k
    bool printed_table_name = false;
392
12.3k
    for (const shared_ptr<YsckTablet>& tablet : table->tablets()) {
393
12.3k
      if (ContainsKey(checksums, tablet->id())) {
394
1.41k
        if (!printed_table_name) {
395
628
          printed_table_name = true;
396
628
          LOG(INFO) << "-----------------------";
397
628
          LOG(INFO) << table->name().ToString();
398
628
          LOG(INFO) << "-----------------------";
399
628
        }
400
1.41k
        bool seen_first_replica = false;
401
1.41k
        uint64_t first_checksum = 0;
402
403
1.41k
        for (const ChecksumResultReporter::ReplicaResultMap::value_type& r :
404
4.19k
                      FindOrDie(checksums, tablet->id())) {
405
4.19k
          const string& replica_uuid = r.first;
406
407
4.19k
          shared_ptr<YsckTabletServer> ts = FindOrDie(cluster_->tablet_servers(), replica_uuid);
408
4.19k
          for (const ChecksumResultReporter::ResultPair& result : r.second) {
409
4.19k
            const Status &status = result.first;
410
4.19k
            uint64_t checksum = result.second;
411
2.90k
            string status_str = (status.ok()) ? Substitute("Checksum: $0", checksum)
412
1.29k
                : Substitute("Error: $0", status.ToString());
413
4.19k
            LOG(INFO) << Substitute("T $0 P $1 ($2): $3",
414
4.19k
                                    tablet->id(), ts->uuid(), ts->address(), status_str);
415
4.19k
            if (!status.ok()) {
416
1.29k
              num_errors++;
417
2.90k
            } else if (!seen_first_replica) {
418
1.11k
              seen_first_replica = true;
419
1.11k
              first_checksum = checksum;
420
1.78k
            } else if (checksum != first_checksum) {
421
1
              num_mismatches++;
422
1
              LOG(ERROR) << ">> Mismatch found in table " << table->name().ToString()
423
1
                         << " tablet " << tablet->id();
424
1
            }
425
4.19k
          }
426
4.19k
          num_results++;
427
4.19k
        }
428
1.41k
      }
429
12.3k
    }
430
11.4k
    if (printed_table_name) LOG(INFO) << "";
431
11.4k
  }
432
628
  if (num_results != num_tablet_replicas) {
433
0
    CHECK(timed_out) << Substitute("Unexpected error: only got $0 out of $1 replica results",
434
0
                                   num_results, num_tablet_replicas);
435
0
    return STATUS(TimedOut, Substitute("Checksum scan did not complete within the timeout of $0: "
436
0
                                       "Received results for $1 out of $2 expected replicas",
437
0
                                       options.timeout.ToString(), num_results,
438
0
                                       num_tablet_replicas));
439
0
  }
440
628
  if (num_mismatches != 0) {
441
1
    return STATUS(Corruption, Substitute("$0 checksum mismatches were detected", num_mismatches));
442
1
  }
443
627
  if (num_errors != 0) {
444
598
    return STATUS(Aborted, Substitute("$0 errors were detected", num_errors));
445
598
  }
446
447
29
  return Status::OK();
448
29
}
449
450
11.5k
bool Ysck::VerifyTable(const shared_ptr<YsckTable>& table) {
451
11.5k
  bool good_table = true;
452
11.5k
  vector<shared_ptr<YsckTablet> > tablets = table->tablets();
453
11.5k
  auto tablets_count = tablets.size();
454
11.5k
  if (tablets_count == 0) {
455
0
    LOG(WARNING) << Substitute("Table $0 has 0 tablets", table->name().ToString());
456
0
    return false;
457
0
  }
458
11.5k
  int table_num_replicas = table->num_replicas();
459
0
  VLOG(1) << Substitute("Verifying $0 tablets for table $1 configured with num_replicas = $2",
460
0
                        tablets_count, table->name().ToString(), table_num_replicas);
461
11.5k
  size_t bad_tablets_count = 0;
462
  // TODO check if the tablets are contiguous and in order.
463
12.5k
  for (const shared_ptr<YsckTablet> &tablet : tablets) {
464
12.5k
    if (!VerifyTablet(tablet, table_num_replicas)) {
465
8
      bad_tablets_count++;
466
8
    }
467
12.5k
  }
468
11.5k
  if (bad_tablets_count == 0) {
469
11.5k
    LOG(INFO) << Substitute("Table $0 is HEALTHY", table->name().ToString());
470
8
  } else {
471
8
    LOG(WARNING) << Substitute("Table $0 has $1 bad tablets",
472
8
                               table->name().ToString(), bad_tablets_count);
473
8
    good_table = false;
474
8
  }
475
11.5k
  return good_table;
476
11.5k
}
477
478
12.5k
bool Ysck::VerifyTablet(const shared_ptr<YsckTablet>& tablet, size_t table_num_replicas) {
479
12.5k
  vector<shared_ptr<YsckTabletReplica> > replicas = tablet->replicas();
480
12.5k
  bool good_tablet = true;
481
12.5k
  if (replicas.size() != table_num_replicas) {
482
12.5k
    LOG(WARNING) << Substitute("Tablet $0 has $1 instead of $2 replicas",
483
12.5k
                               tablet->id(), replicas.size(), table_num_replicas);
484
    // We only fail the "goodness" check if the tablet is under-replicated.
485
12.5k
    if (replicas.size() < table_num_replicas) {
486
0
      good_tablet = false;
487
0
    }
488
12.5k
  }
489
12.5k
  int leaders_count = 0;
490
12.5k
  int followers_count = 0;
491
16.0k
  for (const shared_ptr<YsckTabletReplica>& replica : replicas) {
492
16.0k
    if (replica->is_leader()) {
493
0
      VLOG(1) << Substitute("Replica at $0 is a LEADER", replica->ts_uuid());
494
12.5k
      leaders_count++;
495
3.47k
    } else if (replica->is_follower()) {
496
0
      VLOG(1) << Substitute("Replica at $0 is a FOLLOWER", replica->ts_uuid());
497
3.47k
      followers_count++;
498
3.47k
    }
499
16.0k
  }
500
12.5k
  if (leaders_count == 0) {
501
8
    LOG(WARNING) << Format("Tablet $0 doesn't have a leader, replicas: $1", tablet->id(), replicas);
502
8
    good_tablet = false;
503
8
  }
504
0
  VLOG(1) << Substitute("Tablet $0 has $1 leader and $2 followers",
505
0
                        tablet->id(), leaders_count, followers_count);
506
12.5k
  return good_tablet;
507
12.5k
}
508
509
0
Status Ysck::CheckAssignments() {
510
  // TODO
511
0
  return STATUS(NotSupported, "CheckAssignments hasn't been implemented");
512
0
}
513
514
8
std::string YsckTabletReplica::ToString() const {
515
8
  return YB_CLASS_TO_STRING(is_leader, is_follower, ts_uuid);
516
8
}
517
518
} // namespace tools
519
} // namespace yb