You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by zh...@apache.org on 2023/01/22 11:57:50 UTC

[kudu] branch master updated: [tools] add --tables flag to 'local_replica delete'

This is an automated email from the ASF dual-hosted git repository.

zhangyifan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new 4f5d0997f [tools] add --tables flag to 'local_replica delete'
4f5d0997f is described below

commit 4f5d0997ff9fe2d7d73e98bcda5b8cf7b80fe578
Author: zhangyifan27 <ch...@163.com>
AuthorDate: Wed Jan 11 12:02:28 2023 +0800

    [tools] add --tables flag to 'local_replica delete'
    
    This patch updates the 'local_replica delete' tool to allow deleting
    multiple tablets by table name. Sometimes it is not convenient for
    users to specify multiple tablet ids, but table names will be much
    easier to get.
    
    In order to support deleting all tablets of specified tables, the
    <tablet_ids> argument is updated to <tablet_id_patterns> which
    supports basic glob syntax.
    
    The default value of --tables flag is empty, which means all tables
    will be included, so the tool can achieve same effect as before if
    this flag was not specified.
    
    A new test is added to verify the new functionality.
    
    Change-Id: I2d862a715f05179b2e8def0d1cdfe58c32299329
    Reviewed-on: http://gerrit.cloudera.org:8080/19411
    Reviewed-by: Alexey Serbin <al...@apache.org>
    Tested-by: Kudu Jenkins
---
 src/kudu/tools/kudu-tool-test.cc            | 160 ++++++++++++++++++++++++++--
 src/kudu/tools/tool_action_local_replica.cc |  67 +++++++++---
 2 files changed, 207 insertions(+), 20 deletions(-)

diff --git a/src/kudu/tools/kudu-tool-test.cc b/src/kudu/tools/kudu-tool-test.cc
index b6a5694e6..7714dcad6 100644
--- a/src/kudu/tools/kudu-tool-test.cc
+++ b/src/kudu/tools/kudu-tool-test.cc
@@ -4227,8 +4227,7 @@ TEST_F(ToolTest, TestLocalReplicaDelete) {
       tablet_id, tserver_dir, encryption_args),
       nullptr, &stderr, nullptr, nullptr);
   ASSERT_TRUE(s.IsRuntimeError());
-  ASSERT_STR_CONTAINS(stderr, "Not found: Could not load tablet metadata");
-  ASSERT_STR_CONTAINS(stderr, "No such file or directory");
+  ASSERT_STR_CONTAINS(stderr, "Not found: specified tablet id (pattern) does not exist");
 
   // Try to remove the same tablet replica again if --ignore_nonexistent
   // is specified. The tool should report success and output information on the
@@ -4238,11 +4237,7 @@ TEST_F(ToolTest, TestLocalReplicaDelete) {
                  "--fs_data_dirs=$1 --ignore_nonexistent $2",
                  tablet_id, tserver_dir, encryption_args),
       &stderr));
-  ASSERT_STR_CONTAINS(stderr, Substitute("ignoring error for tablet replica $0 "
-                                         "because of the --ignore_nonexistent flag",
-                                         tablet_id));
-  ASSERT_STR_CONTAINS(stderr, "Not found: Could not load tablet metadata");
-  ASSERT_STR_CONTAINS(stderr, "No such file or directory");
+  ASSERT_STR_CONTAINS(stderr, "ignoring some non-existent or mismatched tablet replicas");
 
   // Verify that the total size of the data on disk after 'delete' action
   // is less than before. Although this doesn't necessarily check
@@ -4305,6 +4300,157 @@ TEST_F(ToolTest, TestLocalReplicaDeleteMultiple) {
   }
 }
 
+// Test 'kudu local_replica delete' tool with --tables flag for deleting
+// tablets by table name.
+TEST_F(ToolTest, TestLocalReplicaDeleteByTableName) {
+  static constexpr int kNumTablets = 10;
+  constexpr const char* const kTableName = "test_table";
+  constexpr const char* const kDefaultTableName = "default_table";
+  NO_FATALS(StartMiniCluster());
+  shared_ptr<KuduClient> client;
+  ASSERT_OK(mini_cluster_->CreateClient(nullptr, &client));
+
+  // TestWorkLoad.Setup() creates a table named 'test_table' with 10 tablets.
+  TestWorkload workload(mini_cluster_.get());
+  workload.set_table_name(kTableName);
+  workload.set_num_replicas(1);
+  workload.set_num_tablets(kNumTablets);
+  workload.Setup();
+
+  // TestWorkLoad.Setup() creates a default table with 1 tablet.
+  workload.set_table_name(kDefaultTableName);
+  workload.set_num_replicas(1);
+  workload.set_num_tablets(1);
+  workload.Setup();
+
+  MiniTabletServer* ts = mini_cluster_->mini_tablet_server(0);
+  vector<string> kudu_tables;
+  ASSERT_OK(client->ListTables(&kudu_tables));
+  ASSERT_EQ(2, kudu_tables.size());
+  {
+    vector<scoped_refptr<TabletReplica>> tablet_replicas;
+    ts->server()->tablet_manager()->GetTabletReplicas(&tablet_replicas);
+    ASSERT_EQ(1 + kNumTablets, tablet_replicas.size());
+  }
+
+  // Tablet server should be shutdown to release the lock and allow operating
+  // on its data.
+  ts->Shutdown();
+  const string& tserver_dir = ts->options()->fs_opts.wal_root;
+  NO_FATALS(RunActionStdoutNone(Substitute(
+      "local_replica delete * --fs_wal_dir=$0 --fs_data_dirs=$0 "
+      "--tables=$1 --clean_unsafe $2", tserver_dir, kTableName,
+      env_->IsEncryptionEnabled() ? GetEncryptionArgs() : "")));
+
+  ASSERT_OK(ts->Start());
+  ASSERT_OK(ts->WaitStarted());
+  vector<scoped_refptr<TabletReplica>> tablet_replicas;
+  ts->server()->tablet_manager()->GetTabletReplicas(&tablet_replicas);
+  ASSERT_EQ(1, tablet_replicas.size());
+  ASSERT_EQ(kDefaultTableName, tablet_replicas[0]->tablet()->metadata()->table_name());
+}
+
+// Test 'kudu local_replica delete' tool with --tables flag for deleting
+// tablets by multiple table names.
+TEST_F(ToolTest, TestLocalReplicaDeleteByMultipleTableNames) {
+  static constexpr int kNumTables = 10;
+  static constexpr int kNumTablets = 10;
+  constexpr const char* const kTableNamePrefix = "test_table";
+  NO_FATALS(StartMiniCluster());
+  shared_ptr<KuduClient> client;
+  ASSERT_OK(mini_cluster_->CreateClient(nullptr, &client));
+
+  vector<string> table_names;
+  // Use TestWorkLoad.Setup() create multiple tables.
+  TestWorkload workload(mini_cluster_.get());
+  for (int i = 0; i < kNumTables; ++i) {
+    string table_name = Substitute("$0_$1", kTableNamePrefix, i);
+    workload.set_table_name(table_name);
+    workload.set_num_replicas(1);
+    workload.set_num_tablets(kNumTablets);
+    workload.Setup();
+    table_names.emplace_back(table_name);
+  }
+
+  MiniTabletServer* ts = mini_cluster_->mini_tablet_server(0);
+  vector<string> kudu_tables;
+  ASSERT_OK(client->ListTables(&kudu_tables));
+  ASSERT_EQ(kNumTables, kudu_tables.size());
+  {
+    vector<scoped_refptr<TabletReplica>> tablet_replicas;
+    ts->server()->tablet_manager()->GetTabletReplicas(&tablet_replicas);
+    ASSERT_EQ(kNumTables * kNumTablets, tablet_replicas.size());
+  }
+
+  // Tablet server should be shutdown to release the lock and allow operating
+  // on its data.
+  ts->Shutdown();
+  const string& tserver_dir = ts->options()->fs_opts.wal_root;
+  const string table_names_csv_str = JoinStrings(table_names, ",");
+  NO_FATALS(RunActionStdoutNone(Substitute(
+      "local_replica delete * --fs_wal_dir=$0 --fs_data_dirs=$0 "
+      "--tables=$1 --clean_unsafe $2", tserver_dir, table_names_csv_str,
+      env_->IsEncryptionEnabled() ? GetEncryptionArgs() : "")));
+
+  ASSERT_OK(ts->Start());
+  ASSERT_OK(ts->WaitStarted());
+  vector<scoped_refptr<TabletReplica>> tablet_replicas;
+  ts->server()->tablet_manager()->GetTabletReplicas(&tablet_replicas);
+  ASSERT_EQ(0, tablet_replicas.size());
+}
+
+// Test 'kudu local_replica delete' tool with non-exist tablet id pattern.
+TEST_F(ToolTest, TestLocalReplicaDeleteWithNonExistentTabletIdPattern) {
+  constexpr const char* const kTableName = "test_table";
+  static constexpr int kNumTablets = 10;
+  NO_FATALS(StartMiniCluster());
+  shared_ptr<KuduClient> client;
+  ASSERT_OK(mini_cluster_->CreateClient(nullptr, &client));
+
+  // TestWorkLoad.Setup() creates a table.
+  TestWorkload workload(mini_cluster_.get());
+  workload.set_table_name(kTableName);
+  workload.set_num_replicas(1);
+  workload.set_num_tablets(kNumTablets);
+  workload.Setup();
+
+  MiniTabletServer* ts = mini_cluster_->mini_tablet_server(0);
+  vector<string> kudu_tables;
+  ASSERT_OK(client->ListTables(&kudu_tables));
+  ASSERT_EQ(1, kudu_tables.size());
+  {
+    vector<scoped_refptr<TabletReplica>> tablet_replicas;
+    ts->server()->tablet_manager()->GetTabletReplicas(&tablet_replicas);
+    ASSERT_EQ(kNumTablets, tablet_replicas.size());
+  }
+
+  // Tablet server should be shutdown to release the lock and allow operating
+  // on its data.
+  ts->Shutdown();
+  const string& tserver_dir = ts->options()->fs_opts.wal_root;
+  // Try to remove tablet replicas by a non-exist tablet id pattern.
+  string stderr;
+  Status s = RunActionStderrString(
+      Substitute("local_replica delete nonexist* --fs_wal_dir=$0 --fs_data_dirs=$0 "
+                 "--clean_unsafe $1",
+                 tserver_dir,
+                 env_->IsEncryptionEnabled() ? GetEncryptionArgs() : ""),
+      &stderr);
+  ASSERT_TRUE(s.IsRuntimeError());
+  ASSERT_STR_CONTAINS(stderr, "Not found: specified tablet id (pattern) does not exist");
+
+  // Try to remove tablet replicas with a non-exist tablet id again if --ignore_nonexistent
+  // is specified. The tool should report success and output information on the
+  // error ignored.
+  ASSERT_OK(RunActionStderrString(
+      Substitute("local_replica delete nonexist* --clean_unsafe --fs_wal_dir=$0 "
+                 "--fs_data_dirs=$0 --ignore_nonexistent $1",
+                 tserver_dir,
+                 env_->IsEncryptionEnabled() ? GetEncryptionArgs() : ""),
+      &stderr));
+  ASSERT_STR_CONTAINS(stderr, "ignoring some non-existent or mismatched tablet replicas");
+}
+
 // Test 'kudu local_replica delete' tool for tombstoning the tablet.
 TEST_F(ToolTest, TestLocalReplicaTombstoneDelete) {
   NO_FATALS(StartMiniCluster());
diff --git a/src/kudu/tools/tool_action_local_replica.cc b/src/kudu/tools/tool_action_local_replica.cc
index 65b02ab83..320b83f3e 100644
--- a/src/kudu/tools/tool_action_local_replica.cc
+++ b/src/kudu/tools/tool_action_local_replica.cc
@@ -158,6 +158,7 @@ DEFINE_string(dst_fs_metadata_dir, "",
 
 DECLARE_int32(num_threads);
 DECLARE_int32(tablet_copy_download_threads_nums_per_session);
+DECLARE_string(tables);
 
 using kudu::consensus::ConsensusMetadata;
 using kudu::consensus::ConsensusMetadataManager;
@@ -229,6 +230,11 @@ constexpr const char* const kTabletIdGlobArgDesc = "Tablet identifier pattern. "
     "This argument supports basic glob syntax: '*' matches 0 or more wildcard "
     "characters.";
 
+constexpr const char* const kTabletIdsGlobArg = "tablet_id_patterns";
+constexpr const char* const kTabletIdsGlobArgDesc = "Comma-separated list of Tablet identifier "
+    "patterns. This argument supports basic glob syntax: '*' matches 0 or more wildcard "
+    "characters.";
+
 constexpr const char* const kRaftPeersArg = "peers";
 constexpr const char* const kRaftPeersArgDesc =
     "List of peers where each element is of form 'uuid:hostname:port', "
@@ -739,21 +745,35 @@ Status DeleteLocalReplica(const string& tablet_id,
   }
 
   scoped_refptr<TabletMetadata> meta;
-  const auto s = TabletMetadata::Load(fs_manager, tablet_id, &meta).AndThen([&]{
+  return TabletMetadata::Load(fs_manager, tablet_id, &meta).AndThen([&]{
     return TSTabletManager::DeleteTabletData(
         meta, cmeta_manager, state, last_logged_opid);
   });
-  if (FLAGS_ignore_nonexistent && s.IsNotFound()) {
-    LOG(INFO) << Substitute("ignoring error for tablet replica $0 because "
-                            "of the --ignore_nonexistent flag: $1",
-                            tablet_id, s.ToString());
+}
+
+Status GetTabletIdsByTableName(FsManager* fs_manager, vector<string>* tablet_ids) {
+  tablet_ids->clear();
+  vector<string> table_filters = Split(FLAGS_tables, ",", strings::SkipEmpty());
+  vector<string> tablets;
+  RETURN_NOT_OK(fs_manager->ListTabletIds(&tablets));
+  if (table_filters.empty()) {
+    tablet_ids->swap(tablets);
     return Status::OK();
   }
-  return s;
+  tablet_ids->reserve(tablets.size());
+  for (const string& tablet_id : tablets) {
+    scoped_refptr<TabletMetadata> tablet_metadata;
+    RETURN_NOT_OK(TabletMetadata::Load(fs_manager, tablet_id, &tablet_metadata));
+    const TabletMetadata& tablet = *tablet_metadata.get();
+    if (MatchesAnyPattern(table_filters, tablet.table_name())) {
+      tablet_ids->emplace_back(tablet_id);
+    }
+  }
+  return Status::OK();
 }
 
 Status DeleteLocalReplicas(const RunnerContext& context) {
-  const string& tablet_ids_str = FindOrDie(context.required_args, kTabletIdsCsvArg);
+  const string& tablet_ids_str = FindOrDie(context.required_args, kTabletIdsGlobArg);
   vector<string> tablet_ids = strings::Split(tablet_ids_str, ",", strings::SkipEmpty());
   if (tablet_ids.empty()) {
     return Status::InvalidArgument("no tablet identifiers provided");
@@ -767,15 +787,35 @@ Status DeleteLocalReplicas(const RunnerContext& context) {
     LOG(INFO) << Substitute("removed $0 duplicate tablet identifiers",
                             orig_count - uniq_count);
   }
-  LOG(INFO) << Substitute("deleting $0 tablets replicas", uniq_count);
 
   FsManager fs_manager(Env::Default(), {});
   RETURN_NOT_OK(fs_manager.Open());
-  scoped_refptr<ConsensusMetadataManager> cmeta_manager(
-      new ConsensusMetadataManager(&fs_manager));
-  for (const auto& tablet_id : tablet_ids) {
-    RETURN_NOT_OK(DeleteLocalReplica(tablet_id, &fs_manager, cmeta_manager));
+
+  vector<string> tablets;
+  RETURN_NOT_OK(GetTabletIdsByTableName(&fs_manager, &tablets));
+
+  int num_tablets_deleted = 0;
+  scoped_refptr<ConsensusMetadataManager> cmeta_manager(new ConsensusMetadataManager(&fs_manager));
+  for (const auto& tablet_id_pattern : tablet_ids) {
+    bool tablet_id_pattern_exists = false;
+    for (const auto& tablet_id : tablets) {
+      if (MatchPattern(tablet_id, tablet_id_pattern)) {
+        tablet_id_pattern_exists = true;
+        RETURN_NOT_OK(DeleteLocalReplica(tablet_id, &fs_manager, cmeta_manager));
+        num_tablets_deleted++;
+      }
+    }
+    if (!FLAGS_ignore_nonexistent && !tablet_id_pattern_exists) {
+      return Status::NotFound(
+          "specified tablet id (pattern) does not exist or does not match "
+          "table name patterns specified in --tables flag.");
+    }
+    if (!tablet_id_pattern_exists) {
+      LOG(INFO) << "ignoring some non-existent or mismatched tablet replicas because of the "
+                   "--ignore_nonexistent flag.";
+    }
   }
+  LOG(INFO) << Substitute("deleted $0 tablet replicas.", num_tablets_deleted);
   return Status::OK();
 }
 
@@ -1358,12 +1398,13 @@ unique_ptr<Mode> BuildLocalReplicaMode() {
       ActionBuilder("delete", &DeleteLocalReplicas)
       .Description("Delete tablet replicas from the local filesystem. "
           "By default, leaves a tombstone record upon replica removal.")
-      .AddRequiredParameter({ kTabletIdsCsvArg, kTabletIdsCsvArgDesc })
+      .AddRequiredParameter({ kTabletIdsGlobArg, kTabletIdsGlobArgDesc })
       .AddOptionalParameter("fs_data_dirs")
       .AddOptionalParameter("fs_metadata_dir")
       .AddOptionalParameter("fs_wal_dir")
       .AddOptionalParameter("clean_unsafe")
       .AddOptionalParameter("ignore_nonexistent")
+      .AddOptionalParameter("tables")
       .Build();
 
   unique_ptr<Action> data_size =