/Users/deen/code/yugabyte-db/src/yb/tools/ts-cli.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 | | // Tool to query tablet server operational data |
33 | | |
34 | | #include <memory> |
35 | | |
36 | | #include <gflags/gflags.h> |
37 | | #include <glog/logging.h> |
38 | | |
39 | | #include "yb/common/partition.h" |
40 | | #include "yb/common/ql_rowblock.h" |
41 | | #include "yb/common/schema.h" |
42 | | #include "yb/common/wire_protocol.h" |
43 | | |
44 | | #include "yb/consensus/consensus.proxy.h" |
45 | | |
46 | | #include "yb/rpc/messenger.h" |
47 | | #include "yb/rpc/proxy.h" |
48 | | #include "yb/rpc/rpc_controller.h" |
49 | | #include "yb/rpc/secure_stream.h" |
50 | | |
51 | | #include "yb/consensus/metadata.pb.h" |
52 | | #include "yb/server/secure.h" |
53 | | #include "yb/server/server_base.proxy.h" |
54 | | |
55 | | #include "yb/tablet/tablet.pb.h" |
56 | | |
57 | | #include "yb/tserver/tablet_server.h" |
58 | | #include "yb/tserver/tserver_admin.proxy.h" |
59 | | #include "yb/tserver/tserver_service.proxy.h" |
60 | | |
61 | | #include "yb/util/faststring.h" |
62 | | #include "yb/util/flags.h" |
63 | | #include "yb/util/logging.h" |
64 | | #include "yb/util/net/net_util.h" |
65 | | #include "yb/util/protobuf_util.h" |
66 | | #include "yb/util/result.h" |
67 | | |
68 | | using std::ostringstream; |
69 | | using std::shared_ptr; |
70 | | using std::string; |
71 | | using std::vector; |
72 | | using yb::HostPort; |
73 | | using yb::consensus::ConsensusServiceProxy; |
74 | | using yb::consensus::RaftConfigPB; |
75 | | using yb::rpc::Messenger; |
76 | | using yb::rpc::MessengerBuilder; |
77 | | using yb::rpc::RpcController; |
78 | | using yb::server::ServerStatusPB; |
79 | | using yb::tablet::TabletStatusPB; |
80 | | using yb::tserver::CountIntentsRequestPB; |
81 | | using yb::tserver::CountIntentsResponsePB; |
82 | | using yb::tserver::DeleteTabletRequestPB; |
83 | | using yb::tserver::DeleteTabletResponsePB; |
84 | | using yb::tserver::FlushTabletsRequestPB; |
85 | | using yb::tserver::FlushTabletsResponsePB; |
86 | | using yb::tserver::IsTabletServerReadyRequestPB; |
87 | | using yb::tserver::IsTabletServerReadyResponsePB; |
88 | | using yb::tserver::ListTabletsRequestPB; |
89 | | using yb::tserver::ListTabletsResponsePB; |
90 | | using yb::tserver::TabletServerAdminServiceProxy; |
91 | | using yb::tserver::TabletServerServiceProxy; |
92 | | |
93 | | const char* const kListTabletsOp = "list_tablets"; |
94 | | const char* const kVerifyTabletOp = "verify_tablet"; |
95 | | const char* const kAreTabletsRunningOp = "are_tablets_running"; |
96 | | const char* const kIsServerReadyOp = "is_server_ready"; |
97 | | const char* const kSetFlagOp = "set_flag"; |
98 | | const char* const kRefreshFlagsOp = "refresh_flags"; |
99 | | const char* const kDumpTabletOp = "dump_tablet"; |
100 | | const char* const kTabletStateOp = "get_tablet_state"; |
101 | | const char* const kDeleteTabletOp = "delete_tablet"; |
102 | | const char* const kUnsafeConfigChange = "unsafe_config_change"; |
103 | | const char* const kCurrentHybridTime = "current_hybrid_time"; |
104 | | const char* const kStatus = "status"; |
105 | | const char* const kCountIntents = "count_intents"; |
106 | | const char* const kFlushTabletOp = "flush_tablet"; |
107 | | const char* const kFlushAllTabletsOp = "flush_all_tablets"; |
108 | | const char* const kCompactTabletOp = "compact_tablet"; |
109 | | const char* const kCompactAllTabletsOp = "compact_all_tablets"; |
110 | | |
111 | | DEFINE_string(server_address, "localhost", |
112 | | "Address of server to run against"); |
113 | | DEFINE_int64(timeout_ms, 1000 * 60, "RPC timeout in milliseconds"); |
114 | | |
115 | | DEFINE_bool(force, false, "set_flag: If true, allows command to set a flag " |
116 | | "which is not explicitly marked as runtime-settable. Such flag changes may be " |
117 | | "simply ignored on the server, or may cause the server to crash.\n" |
118 | | "delete_tablet: If true, command will delete the tablet and remove the tablet " |
119 | | "from the memory, otherwise tablet metadata will be kept in memory with state " |
120 | | "TOMBSTONED."); |
121 | | |
122 | | DEFINE_string(certs_dir_name, "", |
123 | | "Directory with certificates to use for secure server connection."); |
124 | | |
125 | | DEFINE_string(client_node_name, "", "Client node name."); |
126 | | |
127 | | PB_ENUM_FORMATTERS(yb::consensus::LeaderLeaseStatus); |
128 | | |
129 | | // Check that the value of argc matches what's expected, otherwise return a |
130 | | // non-zero exit code. Should be used in main(). |
131 | | #define CHECK_ARGC_OR_RETURN_WITH_USAGE(op, expected) \ |
132 | 7 | do { \ |
133 | 7 | const string& _op = (op); \ |
134 | 7 | const int _expected = (expected); \ |
135 | 7 | if (argc != _expected) { \ |
136 | 0 | /* We substract 2 from _expected because we don't want to count argv[0] or [1]. */ \ |
137 | 0 | std::cerr << "Invalid number of arguments for " << _op \ |
138 | 0 | << ": expected " << (_expected - 2) << " arguments" << std::endl; \ |
139 | 0 | google::ShowUsageWithFlagsRestrict(argv[0], __FILE__); \ |
140 | 0 | return 2; \ |
141 | 0 | } \ |
142 | 7 | } while (0); |
143 | | |
144 | | // Invoke 'to_call' and check its result. If it failed, print 'to_prepend' and |
145 | | // the error to cerr and return a non-zero exit code. Should be used in main(). |
146 | | #define RETURN_NOT_OK_PREPEND_FROM_MAIN(to_call, to_prepend) \ |
147 | 74 | do { \ |
148 | 74 | ::yb::Status s = (to_call); \ |
149 | 74 | if (!s.ok()) { \ |
150 | 0 | std::cerr << (to_prepend) << ": " << s.ToString() << std::endl; \ |
151 | 0 | return 1; \ |
152 | 0 | } \ |
153 | 74 | } while (0); |
154 | | |
155 | | namespace yb { |
156 | | namespace tools { |
157 | | |
158 | | typedef ListTabletsResponsePB::StatusAndSchemaPB StatusAndSchemaPB; |
159 | | |
160 | | class TsAdminClient { |
161 | | public: |
162 | | // Creates an admin client for host/port combination e.g., |
163 | | // "localhost" or "127.0.0.1:7050". |
164 | | TsAdminClient(std::string addr, int64_t timeout_millis); |
165 | | |
166 | | ~TsAdminClient(); |
167 | | |
168 | | // Initialized the client and connects to the specified tablet |
169 | | // server. |
170 | | Status Init(); |
171 | | |
172 | | // Sets 'tablets' a list of status information for all tablets on a |
173 | | // given tablet server. |
174 | | Status ListTablets(std::vector<StatusAndSchemaPB>* tablets); |
175 | | |
176 | | // Gets the number of tablets waiting to be bootstrapped and prints to console. |
177 | | Status GetNumUnbootstrappedTablets(int64_t* num_unbootstrapped_tablets); |
178 | | |
179 | | // Sets the gflag 'flag' to 'val' on the remote server via RPC. |
180 | | // If 'force' is true, allows setting flags even if they're not marked as |
181 | | // safe to change at runtime. |
182 | | Status SetFlag(const string& flag, const string& val, |
183 | | bool force); |
184 | | |
185 | | // Refreshes all gflags on the remote server to the flagfile, via RPC. |
186 | | Status RefreshFlags(); |
187 | | |
188 | | // Get the schema for the given tablet. |
189 | | Status GetTabletSchema(const std::string& tablet_id, SchemaPB* schema); |
190 | | |
191 | | // Dump the contents of the given tablet, in key order, to the console. |
192 | | Status DumpTablet(const std::string& tablet_id); |
193 | | |
194 | | // Print the consensus state to the console. |
195 | | Status PrintConsensusState(const std::string& tablet_id); |
196 | | |
197 | | // Delete a tablet replica from the specified peer. |
198 | | // The 'reason' string is passed to the tablet server, used for logging. |
199 | | Status DeleteTablet(const std::string& tablet_id, |
200 | | const std::string& reason, |
201 | | tablet::TabletDataState delete_type); |
202 | | |
203 | | Status UnsafeConfigChange(const std::string& tablet_id, |
204 | | const std::vector<string>& peers); |
205 | | |
206 | | |
207 | | // Sets hybrid_time to the value of the tablet server's current hybrid_time. |
208 | | Status CurrentHybridTime(uint64_t* hybrid_time); |
209 | | |
210 | | // Get the server status |
211 | | Status GetStatus(ServerStatusPB* pb); |
212 | | |
213 | | // Count write intents on all tablets. |
214 | | Status CountIntents(int64_t* num_intents); |
215 | | |
216 | | // Flush or compact a given tablet on a given tablet server. |
217 | | // If 'tablet_id' is empty string, flush or compact all tablets. |
218 | | Status FlushTablets(const std::string& tablet_id, bool is_compaction); |
219 | | |
220 | | // Verify the given tablet against its indexes |
221 | | // Assume the tablet belongs to a main table |
222 | | Status VerifyTablet( |
223 | | const std::string& tablet_id, |
224 | | const std::vector<string>& index_ids, |
225 | | const string& start_key, |
226 | | const int num_rows); |
227 | | |
228 | | private: |
229 | | std::string addr_; |
230 | | MonoDelta timeout_; |
231 | | bool initted_; |
232 | | std::unique_ptr<rpc::SecureContext> secure_context_; |
233 | | std::unique_ptr<rpc::Messenger> messenger_; |
234 | | shared_ptr<server::GenericServiceProxy> generic_proxy_; |
235 | | std::unique_ptr<tserver::TabletServerServiceProxy> ts_proxy_; |
236 | | std::unique_ptr<tserver::TabletServerAdminServiceProxy> ts_admin_proxy_; |
237 | | std::unique_ptr<consensus::ConsensusServiceProxy> cons_proxy_; |
238 | | |
239 | | DISALLOW_COPY_AND_ASSIGN(TsAdminClient); |
240 | | }; |
241 | | |
242 | | TsAdminClient::TsAdminClient(string addr, int64_t timeout_millis) |
243 | | : addr_(std::move(addr)), |
244 | | timeout_(MonoDelta::FromMilliseconds(timeout_millis)), |
245 | 13 | initted_(false) {} |
246 | | |
247 | 13 | TsAdminClient::~TsAdminClient() { |
248 | 13 | if (messenger_) { |
249 | 13 | messenger_->Shutdown(); |
250 | 13 | } |
251 | 13 | } |
252 | | |
253 | 13 | Status TsAdminClient::Init() { |
254 | 13 | CHECK(!initted_); |
255 | | |
256 | 13 | HostPort host_port; |
257 | 13 | RETURN_NOT_OK(host_port.ParseString(addr_, tserver::TabletServer::kDefaultPort)); |
258 | 13 | auto messenger_builder = MessengerBuilder("ts-cli"); |
259 | 13 | if (!FLAGS_certs_dir_name.empty()) { |
260 | 2 | const std::string& cert_name = FLAGS_client_node_name; |
261 | 2 | secure_context_ = VERIFY_RESULT(server::CreateSecureContext( |
262 | 0 | FLAGS_certs_dir_name, server::UseClientCerts(!cert_name.empty()), cert_name)); |
263 | 0 | server::ApplySecureContext(secure_context_.get(), &messenger_builder); |
264 | 2 | } |
265 | 13 | messenger_ = VERIFY_RESULT(messenger_builder.Build()); |
266 | | |
267 | 0 | rpc::ProxyCache proxy_cache(messenger_.get()); |
268 | | |
269 | 13 | generic_proxy_.reset(new server::GenericServiceProxy(&proxy_cache, host_port)); |
270 | 13 | ts_proxy_.reset(new TabletServerServiceProxy(&proxy_cache, host_port)); |
271 | 13 | ts_admin_proxy_.reset(new TabletServerAdminServiceProxy(&proxy_cache, host_port)); |
272 | 13 | cons_proxy_.reset(new ConsensusServiceProxy(&proxy_cache, host_port)); |
273 | 13 | initted_ = true; |
274 | | |
275 | 13 | VLOG(1) << "Connected to " << addr_0 ; |
276 | | |
277 | 13 | return Status::OK(); |
278 | 13 | } |
279 | | |
280 | 2 | Status TsAdminClient::ListTablets(vector<StatusAndSchemaPB>* tablets) { |
281 | 2 | CHECK(initted_); |
282 | | |
283 | 2 | ListTabletsRequestPB req; |
284 | 2 | ListTabletsResponsePB resp; |
285 | 2 | RpcController rpc; |
286 | | |
287 | 2 | rpc.set_timeout(timeout_); |
288 | 2 | RETURN_NOT_OK(ts_proxy_->ListTablets(req, &resp, &rpc)); |
289 | 2 | if (resp.has_error()) { |
290 | 0 | return StatusFromPB(resp.error().status()); |
291 | 0 | } |
292 | | |
293 | 2 | tablets->assign(resp.status_and_schema().begin(), resp.status_and_schema().end()); |
294 | | |
295 | 2 | return Status::OK(); |
296 | 2 | } |
297 | | |
298 | 0 | Status TsAdminClient::GetNumUnbootstrappedTablets(int64_t* num_unbootstrapped_tablets) { |
299 | 0 | CHECK(initted_); |
300 | |
|
301 | 0 | IsTabletServerReadyRequestPB req; |
302 | 0 | IsTabletServerReadyResponsePB resp; |
303 | 0 | RpcController rpc; |
304 | |
|
305 | 0 | rpc.set_timeout(timeout_); |
306 | 0 | RETURN_NOT_OK(ts_proxy_->IsTabletServerReady(req, &resp, &rpc)); |
307 | 0 | if (resp.has_error()) { |
308 | 0 | return StatusFromPB(resp.error().status()); |
309 | 0 | } |
310 | | |
311 | 0 | std::cout << resp.num_tablets_not_running() << "/" << resp.total_tablets() |
312 | 0 | << " tablets are not yet bootstrapped" << std::endl; |
313 | 0 | *num_unbootstrapped_tablets = resp.num_tablets_not_running(); |
314 | |
|
315 | 0 | return Status::OK(); |
316 | 0 | } |
317 | | |
318 | | Status TsAdminClient::SetFlag(const string& flag, const string& val, |
319 | 2 | bool force) { |
320 | 2 | server::SetFlagRequestPB req; |
321 | 2 | server::SetFlagResponsePB resp; |
322 | 2 | RpcController rpc; |
323 | | |
324 | 2 | rpc.set_timeout(timeout_); |
325 | 2 | req.set_flag(flag); |
326 | 2 | req.set_value(val); |
327 | 2 | req.set_force(force); |
328 | | |
329 | 2 | RETURN_NOT_OK(generic_proxy_->SetFlag(req, &resp, &rpc)); |
330 | 2 | switch (resp.result()) { |
331 | 2 | case server::SetFlagResponsePB::SUCCESS: |
332 | 2 | return Status::OK(); |
333 | 0 | case server::SetFlagResponsePB::NOT_SAFE: |
334 | 0 | return STATUS(RemoteError, resp.msg() + " (use --force flag to allow anyway)"); |
335 | 0 | default: |
336 | 0 | return STATUS(RemoteError, resp.ShortDebugString()); |
337 | 2 | } |
338 | 2 | } |
339 | | |
340 | 2 | Status TsAdminClient::RefreshFlags() { |
341 | 2 | server::RefreshFlagsRequestPB req; |
342 | 2 | server::RefreshFlagsResponsePB resp; |
343 | 2 | RpcController rpc; |
344 | | |
345 | 2 | rpc.set_timeout(timeout_); |
346 | | |
347 | 2 | return generic_proxy_->RefreshFlags(req, &resp, &rpc); |
348 | 2 | } |
349 | | |
350 | | Status TsAdminClient::VerifyTablet( |
351 | | const std::string& tablet_id, |
352 | | const std::vector<string>& index_ids, |
353 | | const string& start_key, |
354 | 0 | const int num_rows) { |
355 | 0 | tserver::VerifyTableRowRangeRequestPB req; |
356 | 0 | tserver::VerifyTableRowRangeResponsePB resp; |
357 | |
|
358 | 0 | req.set_tablet_id(tablet_id); |
359 | 0 | req.set_start_key(""); |
360 | 0 | req.set_num_rows(num_rows); |
361 | 0 | for (const std::string& str : index_ids) { |
362 | 0 | req.add_index_ids(str); |
363 | 0 | } |
364 | |
|
365 | 0 | RpcController rpc; |
366 | 0 | rpc.set_timeout(timeout_); |
367 | |
|
368 | 0 | RETURN_NOT_OK(ts_proxy_->VerifyTableRowRange(req, &resp, &rpc)); |
369 | 0 | if (resp.has_error()) { |
370 | 0 | return StatusFromPB(resp.error().status()); |
371 | 0 | } |
372 | | |
373 | 0 | std::cout << "Reporting VerifyJob stats." << std::endl; |
374 | 0 | for (auto it = resp.consistency_stats().begin(); it != resp.consistency_stats().end(); it++) { |
375 | 0 | std::cout << "VerifyJob found " << it->second << " mismatched rows for index " << it->first |
376 | 0 | << std::endl; |
377 | 0 | } |
378 | |
|
379 | 0 | return Status::OK(); |
380 | 0 | } |
381 | | |
382 | | Status TsAdminClient::GetTabletSchema(const std::string& tablet_id, |
383 | 0 | SchemaPB* schema) { |
384 | 0 | VLOG(1) << "Fetching schema for tablet " << tablet_id; |
385 | 0 | vector<StatusAndSchemaPB> tablets; |
386 | 0 | RETURN_NOT_OK(ListTablets(&tablets)); |
387 | 0 | for (const StatusAndSchemaPB& pair : tablets) { |
388 | 0 | if (pair.tablet_status().tablet_id() == tablet_id) { |
389 | 0 | *schema = pair.schema(); |
390 | 0 | return Status::OK(); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | return STATUS(NotFound, "Cannot find tablet", tablet_id); |
394 | 0 | } |
395 | | |
396 | 0 | Status TsAdminClient::PrintConsensusState(const std::string& tablet_id) { |
397 | 0 | ServerStatusPB status_pb; |
398 | 0 | RETURN_NOT_OK(GetStatus(&status_pb)); |
399 | | |
400 | 0 | consensus::GetConsensusStateRequestPB cons_reqpb; |
401 | 0 | cons_reqpb.set_dest_uuid(status_pb.node_instance().permanent_uuid()); |
402 | 0 | cons_reqpb.set_tablet_id(tablet_id); |
403 | |
|
404 | 0 | consensus::GetConsensusStateResponsePB cons_resp_pb; |
405 | 0 | RpcController rpc; |
406 | 0 | RETURN_NOT_OK_PREPEND( |
407 | 0 | cons_proxy_->GetConsensusState(cons_reqpb, &cons_resp_pb, &rpc), |
408 | 0 | "Failed to query tserver for consensus state"); |
409 | 0 | std::cout << "Lease-Status" |
410 | 0 | << "\t\t" |
411 | 0 | << " Leader-UUID "; |
412 | 0 | std::cout << PBEnumToString(cons_resp_pb.leader_lease_status()) << "\t\t" |
413 | 0 | << cons_resp_pb.cstate().leader_uuid(); |
414 | |
|
415 | 0 | return Status::OK(); |
416 | 0 | } |
417 | | |
418 | 0 | Status TsAdminClient::DumpTablet(const std::string& tablet_id) { |
419 | 0 | SchemaPB schema_pb; |
420 | 0 | RETURN_NOT_OK(GetTabletSchema(tablet_id, &schema_pb)); |
421 | 0 | Schema schema; |
422 | 0 | RETURN_NOT_OK(SchemaFromPB(schema_pb, &schema)); |
423 | | |
424 | 0 | tserver::ReadRequestPB req; |
425 | 0 | tserver::ReadResponsePB resp; |
426 | |
|
427 | 0 | req.set_tablet_id(tablet_id); |
428 | 0 | RpcController rpc; |
429 | 0 | rpc.set_timeout(timeout_); |
430 | 0 | RETURN_NOT_OK_PREPEND(ts_proxy_->Read(req, &resp, &rpc), "Read() failed"); |
431 | |
|
432 | 0 | if (resp.has_error()) { |
433 | 0 | return STATUS(IOError, "Failed to read: ", resp.error().ShortDebugString()); |
434 | 0 | } |
435 | | |
436 | 0 | QLRowBlock row_block(schema); |
437 | 0 | Slice data = VERIFY_RESULT(rpc.GetSidecar(0)); |
438 | 0 | if (!data.empty()) { |
439 | 0 | RETURN_NOT_OK(row_block.Deserialize(YQL_CLIENT_CQL, &data)); |
440 | 0 | } |
441 | | |
442 | 0 | for (const auto& row : row_block.rows()) { |
443 | 0 | std::cout << row.ToString() << std::endl; |
444 | 0 | } |
445 | |
|
446 | 0 | return Status::OK(); |
447 | 0 | } |
448 | | |
449 | | Status TsAdminClient::DeleteTablet(const string& tablet_id, |
450 | | const string& reason, |
451 | 1 | tablet::TabletDataState delete_type) { |
452 | 1 | ServerStatusPB status_pb; |
453 | 1 | RETURN_NOT_OK(GetStatus(&status_pb)); |
454 | | |
455 | 1 | DeleteTabletRequestPB req; |
456 | 1 | DeleteTabletResponsePB resp; |
457 | 1 | RpcController rpc; |
458 | | |
459 | 1 | req.set_tablet_id(tablet_id); |
460 | 1 | req.set_dest_uuid(status_pb.node_instance().permanent_uuid()); |
461 | 1 | req.set_reason(reason); |
462 | 1 | req.set_delete_type(delete_type); |
463 | 1 | rpc.set_timeout(timeout_); |
464 | 1 | RETURN_NOT_OK_PREPEND(ts_admin_proxy_->DeleteTablet(req, &resp, &rpc), |
465 | 1 | "DeleteTablet() failed"); |
466 | | |
467 | 1 | if (resp.has_error()) { |
468 | 0 | return STATUS(IOError, "Failed to delete tablet: ", |
469 | 0 | resp.error().ShortDebugString()); |
470 | 0 | } |
471 | 1 | return Status::OK(); |
472 | 1 | } |
473 | | |
474 | | Status TsAdminClient::UnsafeConfigChange(const std::string& tablet_id, |
475 | 6 | const std::vector<string>& peers) { |
476 | 6 | ServerStatusPB status_pb; |
477 | 6 | RETURN_NOT_OK(GetStatus(&status_pb)); |
478 | | |
479 | 6 | if (peers.empty()) { |
480 | 0 | return STATUS(InvalidArgument, "No peer UUIDs specified for the new config"); |
481 | 0 | } |
482 | 6 | RaftConfigPB new_config; |
483 | 7 | for (const auto& arg : peers) { |
484 | 7 | consensus::RaftPeerPB new_peer; |
485 | 7 | new_peer.set_permanent_uuid(arg); |
486 | 7 | new_config.add_peers()->CopyFrom(new_peer); |
487 | 7 | } |
488 | | |
489 | | // Send a request to replace the config to node dst_address. |
490 | 6 | consensus::UnsafeChangeConfigRequestPB req; |
491 | 6 | consensus::UnsafeChangeConfigResponsePB resp; |
492 | 6 | RpcController rpc; |
493 | 6 | rpc.set_timeout(timeout_); |
494 | 6 | req.set_dest_uuid(status_pb.node_instance().permanent_uuid()); |
495 | 6 | req.set_tablet_id(tablet_id); |
496 | 6 | req.set_caller_id("yb-ts-cli"); |
497 | 6 | *req.mutable_new_config() = new_config; |
498 | 6 | RETURN_NOT_OK(cons_proxy_->UnsafeChangeConfig(req, &resp, &rpc)); |
499 | 6 | if (resp.has_error()) { |
500 | 0 | return StatusFromPB(resp.error().status()); |
501 | 0 | } |
502 | 6 | return Status::OK(); |
503 | 6 | } |
504 | | |
505 | 0 | Status TsAdminClient::CurrentHybridTime(uint64_t* hybrid_time) { |
506 | 0 | server::ServerClockRequestPB req; |
507 | 0 | server::ServerClockResponsePB resp; |
508 | 0 | RpcController rpc; |
509 | 0 | rpc.set_timeout(timeout_); |
510 | 0 | RETURN_NOT_OK(generic_proxy_->ServerClock(req, &resp, &rpc)); |
511 | 0 | CHECK(resp.has_hybrid_time()) << resp.DebugString(); |
512 | 0 | *hybrid_time = resp.hybrid_time(); |
513 | 0 | return Status::OK(); |
514 | 0 | } |
515 | | |
516 | 7 | Status TsAdminClient::GetStatus(ServerStatusPB* pb) { |
517 | 7 | server::GetStatusRequestPB req; |
518 | 7 | server::GetStatusResponsePB resp; |
519 | 7 | RpcController rpc; |
520 | 7 | rpc.set_timeout(timeout_); |
521 | 7 | RETURN_NOT_OK(generic_proxy_->GetStatus(req, &resp, &rpc)); |
522 | 7 | CHECK(resp.has_status()) << resp.DebugString()0 ; |
523 | 7 | pb->Swap(resp.mutable_status()); |
524 | 7 | return Status::OK(); |
525 | 7 | } |
526 | | |
527 | 0 | Status TsAdminClient::CountIntents(int64_t* num_intents) { |
528 | 0 | CountIntentsRequestPB req; |
529 | 0 | CountIntentsResponsePB resp; |
530 | 0 | RpcController rpc; |
531 | 0 | rpc.set_timeout(timeout_); |
532 | 0 | RETURN_NOT_OK(ts_admin_proxy_->CountIntents(req, &resp, &rpc)); |
533 | 0 | *num_intents = resp.num_intents(); |
534 | 0 | return Status::OK(); |
535 | 0 | } |
536 | | |
537 | 0 | Status TsAdminClient::FlushTablets(const std::string& tablet_id, bool is_compaction) { |
538 | 0 | ServerStatusPB status_pb; |
539 | 0 | RETURN_NOT_OK(GetStatus(&status_pb)); |
540 | | |
541 | 0 | FlushTabletsRequestPB req; |
542 | 0 | FlushTabletsResponsePB resp; |
543 | 0 | RpcController rpc; |
544 | |
|
545 | 0 | if (!tablet_id.empty()) { |
546 | 0 | req.add_tablet_ids(tablet_id); |
547 | 0 | req.set_all_tablets(false); |
548 | 0 | } else { |
549 | 0 | req.set_all_tablets(true); |
550 | 0 | } |
551 | 0 | req.set_dest_uuid(status_pb.node_instance().permanent_uuid()); |
552 | 0 | req.set_operation(is_compaction ? tserver::FlushTabletsRequestPB::COMPACT |
553 | 0 | : tserver::FlushTabletsRequestPB::FLUSH); |
554 | 0 | rpc.set_timeout(timeout_); |
555 | 0 | RETURN_NOT_OK_PREPEND(ts_admin_proxy_->FlushTablets(req, &resp, &rpc), |
556 | 0 | "FlushTablets() failed"); |
557 | |
|
558 | 0 | if (resp.has_error()) { |
559 | 0 | return STATUS(IOError, "Failed to flush tablet: ", |
560 | 0 | resp.error().ShortDebugString()); |
561 | 0 | } |
562 | 0 | std::cout << "Successfully " << (is_compaction ? "compacted " : "flushed ") |
563 | 0 | << (tablet_id.empty() ? "all tablets" : "tablet <" + tablet_id + ">") |
564 | 0 | << std::endl; |
565 | 0 | return Status::OK(); |
566 | 0 | } |
567 | | |
568 | | namespace { |
569 | | |
570 | 13 | void SetUsage(const char* argv0) { |
571 | 13 | ostringstream str; |
572 | | |
573 | 13 | str << argv0 << " [--server_address=<addr>] <operation> <flags>\n" |
574 | 13 | << "<operation> must be one of:\n" |
575 | 13 | << " " << kListTabletsOp << "\n" |
576 | 13 | << " " << kAreTabletsRunningOp << "\n" |
577 | 13 | << " " << kIsServerReadyOp << "\n" |
578 | 13 | << " " << kSetFlagOp << " [-force] <flag> <value>\n" |
579 | 13 | << " " << kRefreshFlagsOp << "\n" |
580 | 13 | << " " << kTabletStateOp << " <tablet_id>\n" |
581 | 13 | << " " << kDumpTabletOp << " <tablet_id>\n" |
582 | 13 | << " " << kDeleteTabletOp << " [-force] <tablet_id> <reason string>\n" |
583 | 13 | << " " << kUnsafeConfigChange << " <tablet_id> <peer1> [<peer2>...]\n" |
584 | 13 | << " " << kCurrentHybridTime << "\n" |
585 | 13 | << " " << kStatus << "\n" |
586 | 13 | << " " << kCountIntents << "\n" |
587 | 13 | << " " << kFlushTabletOp << " <tablet_id>\n" |
588 | 13 | << " " << kFlushAllTabletsOp << "\n" |
589 | 13 | << " " << kCompactTabletOp << " <tablet_id>\n" |
590 | 13 | << " " << kCompactAllTabletsOp << "\n" |
591 | 13 | << " " << kVerifyTabletOp |
592 | 13 | << " <tablet_id> <number of indexes> <index list> <start_key> <number of rows>\n"; |
593 | 13 | google::SetUsageMessage(str.str()); |
594 | 13 | } |
595 | | |
596 | 13 | string GetOp(int argc, char** argv) { |
597 | 13 | if (argc < 2) { |
598 | 0 | google::ShowUsageWithFlagsRestrict(argv[0], __FILE__); |
599 | 0 | exit(1); |
600 | 0 | } |
601 | | |
602 | 13 | return argv[1]; |
603 | 13 | } |
604 | | |
605 | | } // anonymous namespace |
606 | | |
607 | 13 | static int TsCliMain(int argc, char** argv) { |
608 | 13 | FLAGS_logtostderr = 1; |
609 | 13 | FLAGS_minloglevel = 2; |
610 | 13 | SetUsage(argv[0]); |
611 | 13 | ParseCommandLineFlags(&argc, &argv, true); |
612 | 13 | InitGoogleLoggingSafe(argv[0]); |
613 | 13 | const string addr = FLAGS_server_address; |
614 | | |
615 | 13 | string op = GetOp(argc, argv); |
616 | | |
617 | 13 | TsAdminClient client(addr, FLAGS_timeout_ms); |
618 | | |
619 | 13 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.Init(), |
620 | 13 | "Unable to establish connection to " + addr); |
621 | | |
622 | | // TODO add other operations here... |
623 | 13 | if (op == kListTabletsOp) { |
624 | 2 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
625 | | |
626 | 2 | vector<StatusAndSchemaPB> tablets; |
627 | 2 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.ListTablets(&tablets), |
628 | 2 | "Unable to list tablets on " + addr); |
629 | 24 | for (const StatusAndSchemaPB& status_and_schema : tablets) { |
630 | 24 | Schema schema; |
631 | 24 | RETURN_NOT_OK_PREPEND_FROM_MAIN(SchemaFromPB(status_and_schema.schema(), &schema), |
632 | 24 | "Unable to deserialize schema from " + addr); |
633 | 24 | PartitionSchema partition_schema; |
634 | 24 | RETURN_NOT_OK_PREPEND_FROM_MAIN(PartitionSchema::FromPB(status_and_schema.partition_schema(), |
635 | 24 | schema, &partition_schema), |
636 | 24 | "Unable to deserialize partition schema from " + addr); |
637 | | |
638 | | |
639 | 24 | TabletStatusPB ts = status_and_schema.tablet_status(); |
640 | | |
641 | 24 | Partition partition; |
642 | 24 | Partition::FromPB(ts.partition(), &partition); |
643 | | |
644 | 24 | string state = tablet::RaftGroupStatePB_Name(ts.state()); |
645 | 24 | std::cout << "Tablet id: " << ts.tablet_id() << std::endl; |
646 | 24 | std::cout << "State: " << state << std::endl; |
647 | 24 | std::cout << "Table name: " << ts.table_name() << std::endl; |
648 | 24 | std::cout << "Partition: " << partition_schema.PartitionDebugString(partition, schema) |
649 | 24 | << std::endl; |
650 | 24 | std::cout << "Schema: " << schema.ToString() << std::endl; |
651 | 24 | } |
652 | 11 | } else if (op == kVerifyTabletOp) { |
653 | 0 | string tablet_id = argv[2]; |
654 | 0 | int num_indexes = std::stoi(argv[3]); |
655 | 0 | std::vector<string> index_ids; |
656 | 0 | for (int i = 0; i < num_indexes; i++) { |
657 | 0 | index_ids.push_back(argv[4 + i]); |
658 | 0 | } |
659 | 0 | string start_key = argv[num_indexes + 4]; |
660 | 0 | int num_rows = std::stoi(argv[num_indexes + 5]); |
661 | |
|
662 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN( |
663 | 0 | client.VerifyTablet(tablet_id, index_ids, start_key, num_rows), |
664 | 0 | "Unable to verify tablet " + tablet_id); |
665 | |
|
666 | 11 | } else if (op == kAreTabletsRunningOp) { |
667 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
668 | |
|
669 | 0 | vector<StatusAndSchemaPB> tablets; |
670 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.ListTablets(&tablets), |
671 | 0 | "Unable to list tablets on " + addr); |
672 | 0 | bool all_running = true; |
673 | 0 | for (const StatusAndSchemaPB& status_and_schema : tablets) { |
674 | 0 | TabletStatusPB ts = status_and_schema.tablet_status(); |
675 | 0 | if (ts.state() != tablet::RUNNING) { |
676 | 0 | std::cout << "Tablet id: " << ts.tablet_id() << " is " |
677 | 0 | << tablet::RaftGroupStatePB_Name(ts.state()) << std::endl; |
678 | 0 | all_running = false; |
679 | 0 | } |
680 | 0 | } |
681 | |
|
682 | 0 | if (all_running) { |
683 | 0 | std::cout << "All tablets are running" << std::endl; |
684 | 0 | } else { |
685 | 0 | std::cout << "Not all tablets are running" << std::endl; |
686 | 0 | return 1; |
687 | 0 | } |
688 | 11 | } else if (op == kIsServerReadyOp) { |
689 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
690 | |
|
691 | 0 | int64_t unbootstrapped_tablets; |
692 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN( |
693 | 0 | client.GetNumUnbootstrappedTablets(&unbootstrapped_tablets), "Unable to read server state"); |
694 | |
|
695 | 0 | if (unbootstrapped_tablets > 0) { |
696 | 0 | std::cout << "Tablet server is not ready" << std::endl; |
697 | 0 | return 1; |
698 | 0 | } else { |
699 | 0 | std::cout << "Tablet server is ready" << std::endl; |
700 | 0 | } |
701 | 11 | } else if (op == kSetFlagOp) { |
702 | 2 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 4); |
703 | | |
704 | 2 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.SetFlag(argv[2], argv[3], FLAGS_force), |
705 | 2 | "Unable to set flag"); |
706 | | |
707 | 9 | } else if (op == kRefreshFlagsOp) { |
708 | 2 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
709 | | |
710 | 2 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.RefreshFlags(), |
711 | 2 | "Unable to refresh flags"); |
712 | 7 | } else if (op == kTabletStateOp) { |
713 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 3); |
714 | |
|
715 | 0 | string tablet_id = argv[2]; |
716 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN( |
717 | 0 | client.PrintConsensusState(tablet_id), "Unable to print tablet state"); |
718 | 7 | } else if (op == kDumpTabletOp) { |
719 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 3); |
720 | |
|
721 | 0 | string tablet_id = argv[2]; |
722 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.DumpTablet(tablet_id), |
723 | 0 | "Unable to dump tablet"); |
724 | 7 | } else if (op == kDeleteTabletOp) { |
725 | 1 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 4); |
726 | | |
727 | 1 | string tablet_id = argv[2]; |
728 | 1 | string reason = argv[3]; |
729 | 1 | tablet::TabletDataState state = FLAGS_force ? tablet::TABLET_DATA_DELETED : |
730 | 1 | tablet::TABLET_DATA_TOMBSTONED0 ; |
731 | 1 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.DeleteTablet(tablet_id, reason, state), |
732 | 1 | "Unable to delete tablet"); |
733 | 6 | } else if (op == kUnsafeConfigChange) { |
734 | 6 | if (argc < 4) { |
735 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 4); |
736 | 0 | } |
737 | | |
738 | 6 | string tablet_id = argv[2]; |
739 | 6 | vector<string> peers; |
740 | 13 | for (int i = 3; i < argc; i++7 ) { |
741 | 7 | peers.push_back(argv[i]); |
742 | 7 | } |
743 | | |
744 | 6 | RETURN_NOT_OK_PREPEND_FROM_MAIN( |
745 | 6 | client.UnsafeConfigChange(tablet_id, peers), "Unable to change config unsafely."); |
746 | 6 | } else if (0 op == kCurrentHybridTime0 ) { |
747 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
748 | |
|
749 | 0 | uint64_t hybrid_time; |
750 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.CurrentHybridTime(&hybrid_time), |
751 | 0 | "Unable to get hybrid_time"); |
752 | 0 | std::cout << hybrid_time << std::endl; |
753 | 0 | } else if (op == kStatus) { |
754 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
755 | |
|
756 | 0 | ServerStatusPB status; |
757 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.GetStatus(&status), |
758 | 0 | "Unable to get status"); |
759 | 0 | std::cout << status.DebugString() << std::endl; |
760 | 0 | } else if (op == kCountIntents) { |
761 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
762 | 0 | int64_t num_intents = 0; |
763 | |
|
764 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.CountIntents(&num_intents), |
765 | 0 | "Unable to count intents"); |
766 | |
|
767 | 0 | std::cout << num_intents << std::endl; |
768 | 0 | } else if (op == kFlushTabletOp) { |
769 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 3); |
770 | |
|
771 | 0 | string tablet_id = argv[2]; |
772 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.FlushTablets(tablet_id, false /* is_compaction */), |
773 | 0 | "Unable to flush tablet"); |
774 | 0 | } else if (op == kFlushAllTabletsOp) { |
775 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
776 | |
|
777 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.FlushTablets(std::string(), false /* is_compaction */), |
778 | 0 | "Unable to flush all tablets"); |
779 | 0 | } else if (op == kCompactTabletOp) { |
780 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 3); |
781 | |
|
782 | 0 | string tablet_id = argv[2]; |
783 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.FlushTablets(tablet_id, true /* is_compaction */), |
784 | 0 | "Unable to compact tablet"); |
785 | 0 | } else if (op == kCompactAllTabletsOp) { |
786 | 0 | CHECK_ARGC_OR_RETURN_WITH_USAGE(op, 2); |
787 | |
|
788 | 0 | RETURN_NOT_OK_PREPEND_FROM_MAIN(client.FlushTablets(std::string(), true /* is_compaction */), |
789 | 0 | "Unable to compact all tablets"); |
790 | 0 | } else { |
791 | 0 | std::cerr << "Invalid operation: " << op << std::endl; |
792 | 0 | google::ShowUsageWithFlagsRestrict(argv[0], __FILE__); |
793 | 0 | return 2; |
794 | 0 | } |
795 | | |
796 | 13 | return 0; |
797 | 13 | } |
798 | | |
799 | | } // namespace tools |
800 | | } // namespace yb |
801 | | |
802 | 18.6k | int main(int argc, char** argv) { |
803 | 18.6k | return yb::tools::TsCliMain(argc, argv); |
804 | 18.6k | } |