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

laiyingchun 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 80c8dde  [tools] Fix schema version incorrctly set to 0 bug after 
rebuilding master
80c8dde is described below

commit 80c8dde02310c94a65bed5acdfc28e332e334569
Author: Yingchun Lai <[email protected]>
AuthorDate: Tue Dec 21 18:15:19 2021 +0800

    [tools] Fix schema version incorrctly set to 0 bug after rebuilding master
    
    If we rebuild master for tables with 0 schema version, we may get
    errors like following on master:
    TS xxx (xxx:xxx) has reported a schema version greater than the current one 
for tablet xxx (table xxx [id=xxx]). Expected version 0 got n (corruption)
    ...
    TS xxx (xxx:xxx): alter failed for tablet xxx (table xxx [id=xxx]),no 
further retry: Invalid argument: Tablet has a newer schema
    or
    TS xxx (xxx:xxx): alter failed for tablet xxx (table xxx [id=xxx]),no 
further retry: Corruption: got a different schema for the same version number
    
    Change-Id: Icf8b7c0c45bcc6160f6eabf977968de5a88ef5c7
    Reviewed-on: http://gerrit.cloudera.org:8080/18112
    Tested-by: Kudu Jenkins
    Reviewed-by: Andrew Wong <[email protected]>
---
 src/kudu/tools/kudu-admin-test.cc    | 61 ++++++++++++++++++++++++++++++++++++
 src/kudu/tools/kudu-tool-test.cc     |  6 ++--
 src/kudu/tools/master_rebuilder.cc   | 40 ++++++++++++++---------
 src/kudu/tools/tool_action_common.cc |  1 +
 src/kudu/tools/tool_action_master.cc |  3 +-
 src/kudu/tserver/tablet_service.cc   |  1 +
 src/kudu/tserver/tserver.proto       |  1 +
 7 files changed, 95 insertions(+), 18 deletions(-)

diff --git a/src/kudu/tools/kudu-admin-test.cc 
b/src/kudu/tools/kudu-admin-test.cc
index cd6c9dd..5f3a5bb 100644
--- a/src/kudu/tools/kudu-admin-test.cc
+++ b/src/kudu/tools/kudu-admin-test.cc
@@ -3033,6 +3033,67 @@ TEST_F(AdminCliTest, TestRebuildMasterWithTombstones) {
   NO_FATALS(cv.CheckCluster());
 }
 
+TEST_F(AdminCliTest, TestRebuildMasterAndAddColumns) {
+  FLAGS_num_tablet_servers = 1;
+  FLAGS_num_replicas = 1;
+
+  NO_FATALS(BuildAndStart());
+
+  {
+    unique_ptr<KuduTableAlterer> 
table_alterer(client_->NewTableAlterer(kTableId));
+    table_alterer->AddColumn("old_column_0")->Type(KuduColumnSchema::INT32);
+    ASSERT_OK(table_alterer->Alter());
+  }
+
+  // Shut down the master and wipe out its data.
+  NO_FATALS(cluster_->master()->Shutdown());
+  ASSERT_OK(cluster_->master()->DeleteFromDisk());
+
+  // Rebuild the master with the tool.
+  string stdout;
+  ASSERT_OK(RunKuduTool(RebuildMasterCmd(*cluster_, /*is_secure*/false, 
/*log_to_stderr*/true),
+                        &stdout));
+  ASSERT_STR_CONTAINS(stdout, "Rebuilt from 1 tablet servers, of which 0 had 
errors");
+  ASSERT_STR_CONTAINS(stdout, "Rebuilt from 1 replicas, of which 0 had 
errors");
+
+  // Restart the master and the tablet servers.
+  // The tablet servers must be restarted so they accept the new master's 
certs.
+  for (int i = 0; i < cluster_->num_tablet_servers(); i++) {
+    cluster_->tablet_server(i)->Shutdown();
+  }
+  ASSERT_OK(cluster_->Restart());
+
+  ASSERT_OK(cluster_->WaitForTabletsRunning(cluster_->tablet_server(0),
+                                            1,
+                                            MonoDelta::FromSeconds(10)));
+
+  // The client has to be rebuilt since there's a new master.
+  KuduClientBuilder builder;
+  ASSERT_OK(cluster_->CreateClient(&builder, &client_));
+
+  // Make sure we can add columns to the table we rebuilt.
+  {
+    unique_ptr<KuduTableAlterer> 
table_alterer(client_->NewTableAlterer(kTableId));
+    table_alterer->AddColumn("new_column_0")->Type(KuduColumnSchema::INT32);
+    ASSERT_OK(table_alterer->Alter());
+  }
+
+  const string& ts_addr = 
cluster_->tablet_server(0)->bound_rpc_addr().ToString();
+  ASSERT_EVENTUALLY([&]() {
+    string stdout;
+    ASSERT_OK(RunKuduTool({"remote_replica", "list", ts_addr, 
"-include_schema"} , &stdout));
+    ASSERT_STR_CONTAINS(stdout, "new_column_0");
+  });
+
+  // Check the altered table is readable.
+  {
+    vector<string> rows;
+    client::sp::shared_ptr<KuduTable> table;
+    ASSERT_OK(client_->OpenTable(kTableId, &table));
+    ScanTableToStrings(table.get(), &rows);
+  }
+}
+
 class SecureClusterAdminCliTest : public KuduTest {
  public:
   void SetUpCluster(bool is_secure) {
diff --git a/src/kudu/tools/kudu-tool-test.cc b/src/kudu/tools/kudu-tool-test.cc
index 67c0193..cb95973 100644
--- a/src/kudu/tools/kudu-tool-test.cc
+++ b/src/kudu/tools/kudu-tool-test.cc
@@ -950,9 +950,9 @@ TEST_F(ToolTest, TestHelpXML) {
   string stderr;
   Status s = RunTool("--helpxml", &stdout, &stderr, nullptr, nullptr);
 
-  ASSERT_TRUE(s.IsRuntimeError());
-  ASSERT_FALSE(stdout.empty());
-  ASSERT_TRUE(stderr.empty());
+  ASSERT_TRUE(s.IsRuntimeError()) << s.ToString();
+  ASSERT_FALSE(stdout.empty()) << stdout;
+  ASSERT_TRUE(stderr.empty()) << stderr;
 
   // All wrapped in AllModes node
   ASSERT_STR_MATCHES(stdout, "<\\?xml 
version=\"1.0\"\\?><AllModes>.*</AllModes>");
diff --git a/src/kudu/tools/master_rebuilder.cc 
b/src/kudu/tools/master_rebuilder.cc
index 27c9e3c..d6267b5 100644
--- a/src/kudu/tools/master_rebuilder.cc
+++ b/src/kudu/tools/master_rebuilder.cc
@@ -25,7 +25,7 @@
 #include <string>
 #include <vector>
 
-#include <gflags/gflags_declare.h>
+#include <gflags/gflags.h>
 #include <glog/logging.h>
 
 #include "kudu/common/common.pb.h"
@@ -56,6 +56,12 @@
 #include "kudu/util/status.h"
 
 DECLARE_int32(default_num_replicas);
+DEFINE_uint32(default_schema_version, 0, "The table schema version assigned to 
tables if one "
+              "cannot be determined automatically. When the tablet server 
version is < 1.16, a "
+              "viable value can be determined manually using "
+              "'kudu pbc dump tablet-meta/<tablet-id>' on each server and 
taking the max value "
+              "found across all tablets. In tablet server versions >= 1.16, 
Kudu will determine "
+              "the proper value for each tablet automatically.");
 
 using kudu::master::Master;
 using kudu::master::MasterOptions;
@@ -178,24 +184,24 @@ Status MasterRebuilder::CheckTableAndTabletConsistency(
 void MasterRebuilder::CreateTable(const 
ListTabletsResponsePB::StatusAndSchemaPB& replica) {
   scoped_refptr<TableInfo> table(new TableInfo(oid_generator_.Next()));
   table->mutable_metadata()->StartMutation();
-  SysTablesEntryPB* table_metadata = 
&table->mutable_metadata()->mutable_dirty()->pb;
+  SysTablesEntryPB* metadata = &table->mutable_metadata()->mutable_dirty()->pb;
   const string& table_name = replica.tablet_status().table_name();
-  table_metadata->set_name(table_name);
+  metadata->set_name(table_name);
 
-  // We can't tell the schema version from ListTablets.
-  // If there's multiple schemas reported by the replicas (i.e. if an alter
-  // table is in progress), we'll fail to recover some replicas and only
-  // partially recover the table.
-  table_metadata->set_version(0);
+  if (!replica.has_schema_version()) {
+    metadata->set_version(FLAGS_default_schema_version);
+  } else {
+    metadata->set_version(replica.schema_version());
+  }
 
   // We can't tell the replication factor from ListTablets.
   // We'll guess the default replication factor because it's safe and almost
   // always correct.
   // TODO(awong): there's probably a better heuristic based on the number of
   // replicas reported by the tablet servers.
-  table_metadata->set_num_replicas(FLAGS_default_num_replicas);
-  table_metadata->mutable_schema()->CopyFrom(replica.schema());
-  
table_metadata->mutable_partition_schema()->CopyFrom(replica.partition_schema());
+  metadata->set_num_replicas(FLAGS_default_num_replicas);
+  metadata->mutable_schema()->CopyFrom(replica.schema());
+  metadata->mutable_partition_schema()->CopyFrom(replica.partition_schema());
 
   int32_t max_column_id = 0;
   for (const auto& column : replica.schema().columns()) {
@@ -203,9 +209,9 @@ void MasterRebuilder::CreateTable(const 
ListTabletsResponsePB::StatusAndSchemaPB
       max_column_id = column.id();
     }
   }
-  table_metadata->set_next_column_id(max_column_id + 1);
-  table_metadata->set_state(SysTablesEntryPB::RUNNING);
-  table_metadata->set_state_msg("reconstructed by MasterRebuilder");
+  metadata->set_next_column_id(max_column_id + 1);
+  metadata->set_state(SysTablesEntryPB::RUNNING);
+  metadata->set_state_msg("reconstructed by MasterRebuilder");
   table->mutable_metadata()->CommitMutation();
   InsertOrDie(&tables_by_name_, table_name, table);
 }
@@ -226,6 +232,12 @@ Status MasterRebuilder::CheckTableConsistency(
     metadata->set_next_column_id(schema_from_replica.max_col_id() + 1);
   }
 
+  // Update schema_version if needed.
+  if (replica.has_schema_version() &&
+      replica.schema_version() > metadata->version()) {
+    metadata->set_version(replica.schema_version());
+  }
+
   // Check the schemas match.
   Schema schema_from_table;
   RETURN_NOT_OK(SchemaFromPB(metadata->schema(), &schema_from_table));
diff --git a/src/kudu/tools/tool_action_common.cc 
b/src/kudu/tools/tool_action_common.cc
index 482357b..a8cfb03 100644
--- a/src/kudu/tools/tool_action_common.cc
+++ b/src/kudu/tools/tool_action_common.cc
@@ -508,6 +508,7 @@ Status GetServerStatus(const string& address, uint16_t 
default_port,
 Status GetReplicas(TabletServerServiceProxy* proxy,
                    vector<ListTabletsResponsePB::StatusAndSchemaPB>* replicas) 
{
   ListTabletsRequestPB req;
+  req.set_need_schema_info(true);
   ListTabletsResponsePB resp;
   RpcController rpc;
   rpc.set_timeout(MonoDelta::FromMilliseconds(FLAGS_timeout_ms));
diff --git a/src/kudu/tools/tool_action_master.cc 
b/src/kudu/tools/tool_action_master.cc
index 919b1c6..4764be1 100644
--- a/src/kudu/tools/tool_action_master.cc
+++ b/src/kudu/tools/tool_action_master.cc
@@ -938,10 +938,11 @@ unique_ptr<Mode> BuildMasterMode() {
         .Description("Rebuild a Kudu master from tablet server metadata")
         .ExtraDescription(rebuild_extra_description)
         .AddRequiredVariadicParameter({ kTabletServerAddressArg, 
kTabletServerAddressDesc })
+        .AddOptionalParameter("default_num_replicas")
+        .AddOptionalParameter("default_schema_version")
         .AddOptionalParameter("fs_data_dirs")
         .AddOptionalParameter("fs_metadata_dir")
         .AddOptionalParameter("fs_wal_dir")
-        .AddOptionalParameter("default_num_replicas")
         .Build();
     builder.AddAction(std::move(unsafe_rebuild));
   }
diff --git a/src/kudu/tserver/tablet_service.cc 
b/src/kudu/tserver/tablet_service.cc
index f7a5c34..d031ebe 100644
--- a/src/kudu/tserver/tablet_service.cc
+++ b/src/kudu/tserver/tablet_service.cc
@@ -2250,6 +2250,7 @@ void TabletServiceImpl::ListTablets(const 
ListTabletsRequestPB* req,
                           status->mutable_schema()));
       CHECK_OK(replica->tablet_metadata()->partition_schema().ToPB(
           replica->tablet_metadata()->schema(), 
status->mutable_partition_schema()));
+      status->set_schema_version(replica->tablet_metadata()->schema_version());
     }
   }
   context->RespondSuccess();
diff --git a/src/kudu/tserver/tserver.proto b/src/kudu/tserver/tserver.proto
index a5d0a9e..d464988 100644
--- a/src/kudu/tserver/tserver.proto
+++ b/src/kudu/tserver/tserver.proto
@@ -205,6 +205,7 @@ message ListTabletsResponsePB {
     // set 'need_schema_info'.
     optional SchemaPB schema = 2;
     optional PartitionSchemaPB partition_schema = 3;
+    optional uint32 schema_version = 4;
   }
 
   repeated StatusAndSchemaPB status_and_schema = 2;

Reply via email to