Skip to content

Commit 7574ecd

Browse files
committed
[yugabyte#23744] docdb: Call TS count callback after loading sys catalog.
Summary: After loading the persisted tablet server registry from the sys catalog, this diff adds logic to arrange for the ts count callback to be called if there are enough tablet servers in the registry. Jira: DB-12649 Test Plan: ``` ./yb_build.sh release --cxx-test-filter-re master_heartbeat-itest --cxx-test master_heartbeat-itest --gtest_filter GlobalTransactionTableCreationTest.CreateGlobalTransactionTableAfterFailover ``` Reviewers: asrivastava Reviewed By: asrivastava Subscribers: ybase, slingam Differential Revision: https://phorge.dev.yugabyte.com/D40001
1 parent a34a5ce commit 7574ecd

File tree

9 files changed

+169
-65
lines changed

9 files changed

+169
-65
lines changed

src/yb/integration-tests/master_heartbeat-itest.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
#include "yb/util/flags.h"
5353
#include "yb/util/tostring.h"
5454

55+
#include "yb/yql/pgwrapper/libpq_utils.h"
56+
5557
using namespace std::literals;
5658

5759
DECLARE_bool(enable_load_balancing);
@@ -672,4 +674,60 @@ TEST_F(MasterHeartbeatITestWithExternal, ReRegisterRemovedPeers) {
672674
original_uuids, 60s, "Wait for master to register original uuids"));
673675
}
674676

677+
// This test class sets up a cluster in an inconsistent state. The tservers have a placement uuid
678+
// set but the masters do not have the placement uuid in the cluster config. This prevents the
679+
// master leader from creating the global transaction status table after registering the tservers
680+
// which is required for a test case.
681+
class GlobalTransactionTableCreationTest : public YBTest {
682+
public:
683+
void SetUp() override;
684+
685+
void TearDown() override;
686+
687+
std::unique_ptr<ExternalMiniCluster> cluster_;
688+
std::string placement_uuid_;
689+
};
690+
691+
TEST_F(GlobalTransactionTableCreationTest, CreateGlobalTransactionTableAfterFailover) {
692+
// In this test we validate the global transaction table is created by the callback scheduled by
693+
// the ts manager at catalog load time when it detects there are enough tservers in the registry.
694+
// Normally the master leader that first registers enough tservers will execute the
695+
// callback. However because this test's setup sets a placement uuid for the tservers without
696+
// setting it in the master's cluster config, the master leader will fail to create the global
697+
// transaction table. After failover we fix the cluster config which unblocks the attempt to
698+
// create the global transaction able.
699+
ASSERT_OK(cluster_->StepDownMasterLeaderAndWaitForNewLeader());
700+
// Sanity check that we cannot create a table yet.
701+
std::string stmt = "CREATE TABLE test_table (k INT PRIMARY KEY, v INT)";
702+
auto pgconn = ASSERT_RESULT(cluster_->ConnectToDB("yugabyte"));
703+
ASSERT_NOK(pgconn.ExecuteFormat(stmt));
704+
master::MasterClusterClient cluster_client(
705+
cluster_->GetLeaderMasterProxy<master::MasterClusterProxy>());
706+
auto config = ASSERT_RESULT(cluster_client.GetMasterClusterConfig());
707+
config.mutable_replication_info()->mutable_live_replicas()->set_placement_uuid(placement_uuid_);
708+
ASSERT_OK(cluster_client.ChangeMasterClusterConfig(std::move(config)));
709+
710+
// Now try to create a table. Table creation through pg will fail unless the transaction table
711+
// already exists.
712+
ASSERT_OK(WaitFor([&pgconn, &stmt]() -> Result<bool> {
713+
return pgconn.ExecuteFormat(stmt).ok();
714+
},
715+
MonoDelta::FromSeconds(60), "Could not create table"));
716+
}
717+
718+
void GlobalTransactionTableCreationTest::SetUp() {
719+
placement_uuid_ = Uuid::Generate().ToString();
720+
auto opts = ExternalMiniClusterOptions();
721+
opts.num_masters = 3;
722+
opts.num_tablet_servers = 3;
723+
opts.enable_ysql = true;
724+
opts.extra_tserver_flags = {Format("--placement_uuid=$0", placement_uuid_)};
725+
cluster_ = std::make_unique<ExternalMiniCluster>(opts);
726+
ASSERT_OK(cluster_->Start());
727+
}
728+
729+
void GlobalTransactionTableCreationTest::TearDown() {
730+
cluster_->Shutdown();
731+
}
732+
675733
} // namespace yb::integration_tests

src/yb/master/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ set(MASTER_SRCS
7676
catalog_entity_info.cc
7777
catalog_entity_tasks.cc
7878
catalog_loaders.cc
79+
catalog_loading_state.cc
7980
catalog_manager.cc
8081
catalog_manager_bg_tasks.cc
8182
catalog_manager_ext.cc

src/yb/master/catalog_loaders.h

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,42 +36,15 @@
3636

3737
#include <boost/preprocessor/cat.hpp>
3838

39-
#include "yb/master/master_fwd.h"
39+
#include "yb/master/catalog_loading_state.h"
4040
#include "yb/master/catalog_manager.h"
41+
#include "yb/master/master_fwd.h"
4142
#include "yb/master/permissions_manager.h"
4243
#include "yb/master/sys_catalog.h"
4344

4445
namespace yb {
4546
namespace master {
4647

47-
struct SysCatalogLoadingState {
48-
std::unordered_map<TableId, std::vector<TableId>> parent_to_child_tables;
49-
std::vector<std::pair<std::function<void()>, std::string>> post_load_tasks;
50-
51-
// The tables which require their memory state to write to disk.
52-
TableIdSet write_to_disk_tables;
53-
54-
// Index tables which require backfill status validation (by checking GC delete markers state).
55-
// The tables are grouped by the indexed table id for performance reasons.
56-
std::unordered_map<TableId, TableIdSet> validate_backfill_status_index_tables;
57-
58-
const LeaderEpoch epoch;
59-
60-
explicit SysCatalogLoadingState(LeaderEpoch leader_epoch = {}) : epoch(std::move(leader_epoch))
61-
{}
62-
63-
void AddPostLoadTask(std::function<void()>&& func, std::string&& msg) {
64-
post_load_tasks.push_back({std::move(func), std::move(msg)});
65-
}
66-
67-
void Reset() {
68-
parent_to_child_tables.clear();
69-
post_load_tasks.clear();
70-
write_to_disk_tables.clear();
71-
validate_backfill_status_index_tables.clear();
72-
}
73-
};
74-
7548
#define DECLARE_LOADER_CLASS(name, key_type, entry_pb_name, mutex) \
7649
class BOOST_PP_CAT(name, Loader) : \
7750
public Visitor<BOOST_PP_CAT(BOOST_PP_CAT(Persistent, name), Info)> { \
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) YugabyteDB, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4+
// in compliance with the License. You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software distributed under the License
9+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10+
// or implied. See the License for the specific language governing permissions and limitations
11+
// under the License.
12+
//
13+
14+
#include "yb/master/catalog_loading_state.h"
15+
16+
namespace yb::master {
17+
18+
SysCatalogLoadingState::SysCatalogLoadingState(LeaderEpoch leader_epoch)
19+
: epoch(std::move(leader_epoch)) {}
20+
21+
void SysCatalogLoadingState::AddPostLoadTask(std::function<void()>&& func, std::string&& msg) {
22+
post_load_tasks.push_back({std::move(func), std::move(msg)});
23+
}
24+
25+
void SysCatalogLoadingState::Reset() {
26+
parent_to_child_tables.clear();
27+
post_load_tasks.clear();
28+
write_to_disk_tables.clear();
29+
validate_backfill_status_index_tables.clear();
30+
}
31+
} // namespace yb::master
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) YugabyteDB, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4+
// in compliance with the License. You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software distributed under the License
9+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10+
// or implied. See the License for the specific language governing permissions and limitations
11+
// under the License.
12+
//
13+
14+
#pragma once
15+
16+
#include "yb/common/entity_ids_types.h"
17+
18+
#include "yb/master/leader_epoch.h"
19+
20+
namespace yb::master {
21+
22+
struct SysCatalogLoadingState {
23+
std::unordered_map<TableId, std::vector<TableId>> parent_to_child_tables;
24+
std::vector<std::pair<std::function<void()>, std::string>> post_load_tasks;
25+
26+
// The tables which require their memory state to write to disk.
27+
TableIdSet write_to_disk_tables;
28+
29+
// Index tables which require backfill status validation (by checking GC delete markers state).
30+
// The tables are grouped by the indexed table id for performance reasons.
31+
std::unordered_map<TableId, TableIdSet> validate_backfill_status_index_tables;
32+
33+
const LeaderEpoch epoch;
34+
35+
explicit SysCatalogLoadingState(LeaderEpoch leader_epoch = {});
36+
37+
void AddPostLoadTask(std::function<void()>&& func, std::string&& msg);
38+
39+
void Reset();
40+
};
41+
} // namespace yb::master

src/yb/master/catalog_manager.cc

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,31 +1320,6 @@ Status CatalogManager::VisitSysCatalog(SysCatalogLoadingState* state) {
13201320
// it's important to end their tasks now.
13211321
AbortAndWaitForAllTasksUnlocked();
13221322

1323-
// Clear internal maps and run data loaders.
1324-
RETURN_NOT_OK(RunLoaders(state));
1325-
1326-
// Prepare various default system configurations.
1327-
RETURN_NOT_OK(PrepareDefaultSysConfig(term));
1328-
1329-
RETURN_NOT_OK(MaybeRestoreInitialSysCatalogSnapshotAndReloadSysCatalog(state));
1330-
1331-
// Create the system namespaces (created only if they don't already exist).
1332-
RETURN_NOT_OK(PrepareDefaultNamespaces(term));
1333-
1334-
// Create the system tables (created only if they don't already exist).
1335-
RETURN_NOT_OK(PrepareSystemTables(state->epoch));
1336-
1337-
// Create the default cassandra (created only if they don't already exist).
1338-
RETURN_NOT_OK(permissions_manager_->PrepareDefaultRoles(term));
1339-
1340-
// If this is the first time we start up, we have no config information as default. We write an
1341-
// empty version 0.
1342-
RETURN_NOT_OK(PrepareDefaultClusterConfig(term));
1343-
1344-
RETURN_NOT_OK(xcluster_manager_->PrepareDefaultXClusterConfig(term, /* recreate = */ false));
1345-
1346-
permissions_manager_->BuildRecursiveRoles();
1347-
13481323
if (FLAGS_enable_ysql) {
13491324
// Number of TS to wait for before creating the txn table.
13501325
auto wait_ts_count = std::max(FLAGS_txn_table_wait_min_ts_count, FLAGS_replication_factor);
@@ -1382,6 +1357,31 @@ Status CatalogManager::VisitSysCatalog(SysCatalogLoadingState* state) {
13821357
});
13831358
}
13841359

1360+
// Clear internal maps and run data loaders.
1361+
RETURN_NOT_OK(RunLoaders(state));
1362+
1363+
// Prepare various default system configurations.
1364+
RETURN_NOT_OK(PrepareDefaultSysConfig(term));
1365+
1366+
RETURN_NOT_OK(MaybeRestoreInitialSysCatalogSnapshotAndReloadSysCatalog(state));
1367+
1368+
// Create the system namespaces (created only if they don't already exist).
1369+
RETURN_NOT_OK(PrepareDefaultNamespaces(term));
1370+
1371+
// Create the system tables (created only if they don't already exist).
1372+
RETURN_NOT_OK(PrepareSystemTables(state->epoch));
1373+
1374+
// Create the default cassandra (created only if they don't already exist).
1375+
RETURN_NOT_OK(permissions_manager_->PrepareDefaultRoles(term));
1376+
1377+
// If this is the first time we start up, we have no config information as default. We write an
1378+
// empty version 0.
1379+
RETURN_NOT_OK(PrepareDefaultClusterConfig(term));
1380+
1381+
RETURN_NOT_OK(xcluster_manager_->PrepareDefaultXClusterConfig(term, /* recreate = */ false));
1382+
1383+
permissions_manager_->BuildRecursiveRoles();
1384+
13851385
if (!VERIFY_RESULT(StartRunningInitDbIfNeeded(state->epoch))) {
13861386
// If we are not running initdb, this is an existing cluster, and we need to check whether we
13871387
// need to do a one-time migration to make YSQL system catalog tables transactional.
@@ -1491,8 +1491,8 @@ Status CatalogManager::RunLoaders(SysCatalogLoadingState* state) {
14911491

14921492
RETURN_NOT_OK(xcluster_manager_->RunLoaders(hidden_tablets_));
14931493
RETURN_NOT_OK(master_->clone_state_manager().ClearAndRunLoaders(state->epoch));
1494-
RETURN_NOT_OK(
1495-
master_->ts_manager()->RunLoader(master_->MakeCloudInfoPB(), &master_->proxy_cache()));
1494+
RETURN_NOT_OK(master_->ts_manager()->RunLoader(
1495+
master_->MakeCloudInfoPB(), &master_->proxy_cache(), *state));
14961496

14971497
return Status::OK();
14981498
}

src/yb/master/catalog_manager_bg_tasks.cc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,7 @@ void CatalogManagerBgTasks::Run() {
366366
}
367367

368368
if (FLAGS_enable_ysql) {
369-
// Start the tablespace background task.
370369
catalog_manager_->StartTablespaceBgTaskIfStopped();
371-
372-
// Start the pg catalog versions background task.
373370
catalog_manager_->StartPgCatalogVersionsBgTaskIfStopped();
374371
}
375372

src/yb/master/ts_manager.cc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,8 @@ Status TSManager::MarkUnresponsiveTServers(const LeaderEpoch& epoch) {
433433
return Status::OK();
434434
}
435435

436-
Status TSManager::RunLoader(const CloudInfoPB& cloud_info, rpc::ProxyCache* proxy_cache) {
436+
Status TSManager::RunLoader(
437+
const CloudInfoPB& cloud_info, rpc::ProxyCache* proxy_cache, SysCatalogLoadingState& state) {
437438
if (!GetAtomicFlag(&FLAGS_persist_tserver_registry)) {
438439
return Status::OK();
439440
}
@@ -442,10 +443,10 @@ Status TSManager::RunLoader(const CloudInfoPB& cloud_info, rpc::ProxyCache* prox
442443
MutexLock l_reg(registration_lock_);
443444
std::lock_guard l_map(map_lock_);
444445
servers_by_id_ = loader->TakeMap();
445-
// todo(zdrudi): cluster can be wedged (permanently?) if we crash after registering enough
446-
// tservers but before calling the callback. so add a check to potentially schedule the callback
447-
// here.
448-
// https://github.com/yugabyte/yugabyte-db/issues/23744
446+
if (servers_by_id_.size() >= ts_count_callback_min_count_ && ts_count_callback_) {
447+
state.AddPostLoadTask(
448+
std::move(ts_count_callback_), "Callback run at minimum number of tservers");
449+
}
449450
return Status::OK();
450451
}
451452

src/yb/master/ts_manager.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "yb/gutil/macros.h"
4343
#include "yb/gutil/thread_annotations.h"
4444

45+
#include "yb/master/catalog_loading_state.h"
4546
#include "yb/master/master_cluster.fwd.h"
4647
#include "yb/master/master_fwd.h"
4748
#include "yb/master/ts_descriptor.h"
@@ -160,7 +161,8 @@ class TSManager {
160161
// Transition all such TServers into the UNRESPONSIVE state.
161162
Status MarkUnresponsiveTServers(const LeaderEpoch& epoch);
162163

163-
Status RunLoader(const CloudInfoPB& cloud_info, rpc::ProxyCache* proxy_cache);
164+
Status RunLoader(
165+
const CloudInfoPB& cloud_info, rpc::ProxyCache* proxy_cache, SysCatalogLoadingState& state);
164166

165167
Status RemoveTabletServer(
166168
const std::string& permanent_uuid, const BlacklistSet& blacklist,

0 commit comments

Comments
 (0)