You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by gr...@apache.org on 2018/10/16 19:56:27 UTC

[3/5] kudu git commit: [tools] Add checking for row existence to the locate_row tool

[tools] Add checking for row existence to the locate_row tool

This enhances the `kudu tablet locate_row` tool to that, in addition to
locating which tablet a row with a given primary key would end up in, it
also will check if the row actually exists when the -check_row_existence
flag is supplied. If the row does not exist, the tool returns an error;
if it does, it prints the row.

Example invocation:

$ bin/kudu table locate_row localhost:7053 default.loadgen_auto_49c97e85f6aa43b5a89723c7eee396b0 "[0]" -check_row_existence
850632cee25b43368cb6f226b53e76eb
(int64 key=0, int32 int_val=1, string string_val="2.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")

Change-Id: I760d3fbb2e30a3ffba7143d3b51da6f3fd62b034
Reviewed-on: http://gerrit.cloudera.org:8080/11684
Tested-by: Will Berkeley <wd...@gmail.com>
Reviewed-by: Alexey Serbin <as...@cloudera.com>


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/acf1dee8
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/acf1dee8
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/acf1dee8

Branch: refs/heads/master
Commit: acf1dee87ce347398ca86cc4959e38bf8794efe3
Parents: fa5a0db
Author: Will Berkeley <wd...@gmail.org>
Authored: Mon Oct 15 01:37:30 2018 -0700
Committer: Will Berkeley <wd...@gmail.com>
Committed: Tue Oct 16 19:41:54 2018 +0000

----------------------------------------------------------------------
 src/kudu/tools/kudu-admin-test.cc   | 70 ++++++++++++++++++++++++++++++++
 src/kudu/tools/tool_action_table.cc | 29 +++++++++++++
 2 files changed, 99 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/acf1dee8/src/kudu/tools/kudu-admin-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/kudu-admin-test.cc b/src/kudu/tools/kudu-admin-test.cc
index edb0c4d..d68fb4b 100644
--- a/src/kudu/tools/kudu-admin-test.cc
+++ b/src/kudu/tools/kudu-admin-test.cc
@@ -39,6 +39,7 @@
 #include "kudu/client/client.h"
 #include "kudu/client/schema.h"
 #include "kudu/client/shared_ptr.h"
+#include "kudu/client/write_op.h"
 #include "kudu/common/common.pb.h"
 #include "kudu/common/partial_row.h"
 #include "kudu/common/wire_protocol.pb.h"
@@ -79,8 +80,10 @@ DECLARE_int32(num_tablet_servers);
 using kudu::client::KuduClient;
 using kudu::client::KuduClientBuilder;
 using kudu::client::KuduColumnSchema;
+using kudu::client::KuduInsert;
 using kudu::client::KuduSchema;
 using kudu::client::KuduSchemaBuilder;
+using kudu::client::KuduTable;
 using kudu::client::KuduTableAlterer;
 using kudu::client::KuduTableCreator;
 using kudu::client::sp::shared_ptr;
@@ -1742,5 +1745,72 @@ TEST_F(AdminCliTest, TestLocateRowMore) {
   ASSERT_STR_CONTAINS(stderr,
                       "Wrong type during field extraction: expected object array");
 }
+
+TEST_F(AdminCliTest, TestLocateRowAndCheckRowPresence) {
+  FLAGS_num_tablet_servers = 1;
+  FLAGS_num_replicas = 1;
+
+  NO_FATALS(BuildAndStart());
+
+  // Grab list of tablet_ids from any tserver so we can check the output.
+  vector<TServerDetails*> tservers;
+  vector<string> tablet_ids;
+  AppendValuesFromMap(tablet_servers_, &tservers);
+  ListRunningTabletIds(tservers.front(),
+                       MonoDelta::FromSeconds(30),
+                       &tablet_ids);
+  ASSERT_EQ(1, tablet_ids.size());
+  const string& expected_tablet_id = tablet_ids[0];
+
+  // Test the case when the row does not exist.
+  string stdout, stderr;
+  Status s = RunKuduTool({
+    "table",
+    "locate_row",
+    cluster_->master()->bound_rpc_addr().ToString(),
+    kTableId,
+    "[0]",
+    "-check_row_existence",
+  }, &stdout, &stderr);
+  ASSERT_TRUE(s.IsRuntimeError()) << ToolRunInfo(s, stdout, stderr);
+  ASSERT_STR_CONTAINS(stdout, expected_tablet_id);
+  ASSERT_STR_CONTAINS(stderr, "row does not exist");
+
+  // Insert row with key = 0.
+  client::sp::shared_ptr<KuduClient> client;
+  CreateClient(&client);
+  client::sp::shared_ptr<KuduTable> table;
+  ASSERT_OK(client->OpenTable(kTableId, &table));
+  unique_ptr<KuduInsert> insert(table->NewInsert());
+  auto* row = insert->mutable_row();
+  ASSERT_OK(row->SetInt32("key", 0));
+  ASSERT_OK(row->SetInt32("int_val", 12345));
+  ASSERT_OK(row->SetString("string_val", "hello"));
+  const string row_str = row->ToString();
+  auto session = client->NewSession();
+  ASSERT_OK(session->Apply(insert.release()));
+  ASSERT_OK(session->Flush());
+  ASSERT_OK(session->Close());
+
+  // Test the case when the row exists. Since the scan is done by a subprocess
+  // using a different client instance, it's possible the scan will not
+  // immediately retrieve the row even though the write has already succeeded,
+  // so we ASSERT_EVENTUALLY.
+  ASSERT_EVENTUALLY([&]() {
+    stdout.clear();
+    stderr.clear();
+    s = RunKuduTool({
+      "table",
+      "locate_row",
+      cluster_->master()->bound_rpc_addr().ToString(),
+      kTableId,
+      "[0]",
+      "-check_row_existence",
+    }, &stdout, &stderr);
+    ASSERT_TRUE(s.ok()) << ToolRunInfo(s, stdout, stderr);
+    ASSERT_STR_CONTAINS(stdout, expected_tablet_id);
+    ASSERT_STR_CONTAINS(stdout, row_str);
+  });
+}
 } // namespace tools
 } // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/acf1dee8/src/kudu/tools/tool_action_table.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_table.cc b/src/kudu/tools/tool_action_table.cc
index 30ce6a6..2156dba 100644
--- a/src/kudu/tools/tool_action_table.cc
+++ b/src/kudu/tools/tool_action_table.cc
@@ -38,6 +38,7 @@
 #include "kudu/common/schema.h"
 #include "kudu/gutil/map-util.h"
 #include "kudu/gutil/stl_util.h"
+#include "kudu/gutil/strings/join.h"
 #include "kudu/gutil/strings/split.h"
 #include "kudu/gutil/strings/substitute.h"
 #include "kudu/tools/tool_action.h"
@@ -46,6 +47,11 @@
 #include "kudu/util/status.h"
 
 DECLARE_string(tables);
+DEFINE_bool(check_row_existence, false,
+            "Also check for the existence of the row on the leader replica of "
+            "the tablet. If found, the full row will be printed; if not found, "
+            "an error message will be printed and the command will return a "
+            "non-zero status.");
 DEFINE_bool(modify_external_catalogs, true,
             "Whether to modify external catalogs, such as the Hive Metastore, "
             "when renaming or dropping a table.");
@@ -59,6 +65,7 @@ using client::KuduClient;
 using client::KuduClientBuilder;
 using client::KuduColumnSchema;
 using client::KuduPredicate;
+using client::KuduScanner;
 using client::KuduScanToken;
 using client::KuduScanTokenBuilder;
 using client::KuduTable;
@@ -268,6 +275,8 @@ Status LocateRow(const RunnerContext& context) {
   vector<KuduScanToken*> tokens;
   ElementDeleter deleter(&tokens);
   KuduScanTokenBuilder builder(table.get());
+  // In case we go on to check for existence of the row.
+  RETURN_NOT_OK(builder.SetSelection(KuduClient::ReplicaSelection::LEADER_ONLY));
   for (auto& predicate : predicates) {
     RETURN_NOT_OK(builder.AddConjunctPredicate(predicate.release()));
   }
@@ -282,6 +291,25 @@ Status LocateRow(const RunnerContext& context) {
         "all primary key columns specified but more than one matching tablet?");
   }
   cout << tokens[0]->tablet().id() << endl;
+
+  if (FLAGS_check_row_existence) {
+    KuduScanner* scanner_ptr;
+    RETURN_NOT_OK(tokens[0]->IntoKuduScanner(&scanner_ptr));
+    unique_ptr<KuduScanner> scanner(scanner_ptr);
+    vector<string> row_str;
+    RETURN_NOT_OK(ScanToStrings(scanner.get(), &row_str));
+    if (row_str.empty()) {
+      return Status::NotFound("row does not exist");
+    }
+    // There should be exactly one result, but if somehow there are more, print
+    // them all before returning an error.
+    cout << JoinStrings(row_str, "\n") << endl;
+    if (row_str.size() != 1) {
+      // This should be impossible.
+      return Status::IllegalState(
+          Substitute("expected 1 row but received $0", row_str.size()));
+    }
+  }
   return Status::OK();
 }
 
@@ -355,6 +383,7 @@ unique_ptr<Mode> BuildTableMode() {
       .AddRequiredParameter({ kKeyArg,
                               "String representation of the row's primary key "
                               "as a JSON array" })
+      .AddOptionalParameter("check_row_existence")
       .Build();
 
   unique_ptr<Action> rename_column =