Skip to content

Commit 5a8b1e9

Browse files
authored
Backup async replication (#14441)
1 parent 7a4c2f7 commit 5a8b1e9

File tree

4 files changed

+134
-1
lines changed

4 files changed

+134
-1
lines changed

ydb/library/backup/backup.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
#include "util.h"
44

55
#include <ydb-cpp-sdk/client/cms/cms.h>
6+
#include <ydb-cpp-sdk/client/draft/ydb_replication.h>
67
#include <ydb-cpp-sdk/client/draft/ydb_view.h>
78
#include <ydb-cpp-sdk/client/driver/driver.h>
89
#include <ydb-cpp-sdk/client/proto/accessor.h>
910
#include <ydb-cpp-sdk/client/result/result.h>
1011
#include <ydb-cpp-sdk/client/table/table.h>
1112
#include <ydb-cpp-sdk/client/topic/client.h>
1213
#include <ydb-cpp-sdk/client/value/value.h>
14+
#include <ydb/public/api/protos/draft/ydb_replication.pb.h>
1315
#include <ydb/public/api/protos/draft/ydb_view.pb.h>
1416
#include <ydb/public/api/protos/ydb_cms.pb.h>
1517
#include <ydb/public/api/protos/ydb_rate_limiter.pb.h>
@@ -52,6 +54,7 @@
5254

5355
#include <google/protobuf/text_format.h>
5456

57+
#include <format>
5558

5659
namespace NYdb::NBackup {
5760

@@ -683,6 +686,110 @@ void BackupCoordinationNode(TDriver driver, const TString& dbPath, const TFsPath
683686
BackupPermissions(driver, dbPath, fsBackupFolder);
684687
}
685688

689+
namespace {
690+
691+
NReplication::TReplicationDescription DescribeReplication(TDriver driver, const TString& path) {
692+
NReplication::TReplicationClient client(driver);
693+
auto status = NConsoleClient::RetryFunction([&]() {
694+
return client.DescribeReplication(path).ExtractValueSync();
695+
});
696+
VerifyStatus(status, "describe async replication");
697+
return status.GetReplicationDescription();
698+
}
699+
700+
TString BuildConnectionString(const NReplication::TConnectionParams& params) {
701+
return TStringBuilder()
702+
<< (params.GetEnableSsl() ? "grpcs://" : "grpc://")
703+
<< params.GetDiscoveryEndpoint()
704+
<< "/?database=" << params.GetDatabase();
705+
}
706+
707+
inline TString BuildTarget(const char* src, const char* dst) {
708+
return TStringBuilder() << " `" << src << "` AS `" << dst << "`";
709+
}
710+
711+
inline TString Quote(const char* value) {
712+
return TStringBuilder() << "'" << value << "'";
713+
}
714+
715+
template <typename StringType>
716+
inline TString Quote(const StringType& value) {
717+
return Quote(value.c_str());
718+
}
719+
720+
inline TString BuildOption(const char* key, const TString& value) {
721+
return TStringBuilder() << " " << key << " = " << value << "";
722+
}
723+
724+
inline TString Interval(const TDuration& value) {
725+
return TStringBuilder() << "Interval('PT" << value.Seconds() << "S')";
726+
}
727+
728+
TString BuildCreateReplicationQuery(
729+
const TString& name,
730+
const TString& dbPath,
731+
const NReplication::TReplicationDescription& desc,
732+
const TString& backupRoot,
733+
NYql::TIssues& issues)
734+
{
735+
// TODO(ilnaz)
736+
Y_UNUSED(dbPath);
737+
Y_UNUSED(backupRoot);
738+
Y_UNUSED(issues);
739+
740+
TVector<TString> targets(::Reserve(desc.GetItems().size()));
741+
for (const auto& item : desc.GetItems()) {
742+
if (!item.DstPath.ends_with("/indexImplTable")) { // TODO(ilnaz): get rid of this hack
743+
targets.push_back(BuildTarget(item.SrcPath.c_str(), item.DstPath.c_str()));
744+
}
745+
}
746+
747+
const auto& params = desc.GetConnectionParams();
748+
749+
TVector<TString> opts(::Reserve(5 /* max options */));
750+
opts.push_back(BuildOption("CONNECTION_STRING", Quote(BuildConnectionString(params))));
751+
switch (params.GetCredentials()) {
752+
case NReplication::TConnectionParams::ECredentials::Static:
753+
opts.push_back(BuildOption("USER", Quote(params.GetStaticCredentials().User)));
754+
opts.push_back(BuildOption("PASSWORD_SECRET_NAME", Quote(params.GetStaticCredentials().PasswordSecretName)));
755+
break;
756+
case NReplication::TConnectionParams::ECredentials::OAuth:
757+
opts.push_back(BuildOption("TOKEN_SECRET_NAME", Quote(params.GetOAuthCredentials().TokenSecretName)));
758+
break;
759+
}
760+
761+
opts.push_back(BuildOption("CONSISTENCY_LEVEL", Quote(ToString(desc.GetConsistencyLevel()))));
762+
if (desc.GetConsistencyLevel() == NReplication::TReplicationDescription::EConsistencyLevel::Global) {
763+
opts.push_back(BuildOption("COMMIT_INTERVAL", Interval(desc.GetGlobalConsistency().GetCommitInterval())));
764+
}
765+
766+
return std::format("CREATE ASYNC REPLICATION `{}`\nFOR\n{}\nWITH (\n{}\n);",
767+
name.c_str(), JoinSeq(",\n", targets).c_str(), JoinSeq(",\n", opts).c_str());
768+
}
769+
770+
}
771+
772+
void BackupReplication(
773+
TDriver driver,
774+
const TString& dbBackupRoot,
775+
const TString& dbPathRelativeToBackupRoot,
776+
const TFsPath& fsBackupFolder,
777+
NYql::TIssues& issues)
778+
{
779+
Y_ENSURE(!dbPathRelativeToBackupRoot.empty());
780+
const auto dbPath = JoinDatabasePath(dbBackupRoot, dbPathRelativeToBackupRoot);
781+
782+
LOG_I("Backup async replication " << dbPath.Quote() << " to " << fsBackupFolder.GetPath().Quote());
783+
784+
const auto name = TFsPath(dbPathRelativeToBackupRoot).GetName();
785+
const auto desc = DescribeReplication(driver, dbPath);
786+
const auto creationQuery = BuildCreateReplicationQuery(name, dbPath, desc, dbBackupRoot, issues);
787+
Y_ENSURE(creationQuery, issues.ToString());
788+
789+
WriteCreationQueryToFile(creationQuery, fsBackupFolder, NDump::NFiles::CreateAsyncReplication());
790+
BackupPermissions(driver, dbPath, fsBackupFolder);
791+
}
792+
686793
void CreateClusterDirectory(const TDriver& driver, const TString& path, bool rootBackupDir = false) {
687794
if (rootBackupDir) {
688795
LOG_I("Create temporary directory " << path.Quote() << " in database");
@@ -776,6 +883,9 @@ void BackupFolderImpl(TDriver driver, const TString& dbPrefix, const TString& ba
776883
if (dbIt.IsCoordinationNode()) {
777884
BackupCoordinationNode(driver, dbIt.GetFullPath(), childFolderPath);
778885
}
886+
if (dbIt.IsReplication()) {
887+
BackupReplication(driver, dbIt.GetTraverseRoot(), dbIt.GetRelPath(), childFolderPath, issues);
888+
}
779889
dbIt.Next();
780890
}
781891
}

ydb/library/backup/db_iterator.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ class TDbIterator {
4040
TString TraverseRoot;
4141
TDeque<TSchemeEntryWithPath> NextNodes;
4242

43+
static const TVector<NScheme::ESchemeEntryType>& SupportedEntryTypes() {
44+
static const TVector<NScheme::ESchemeEntryType> values = {
45+
NScheme::ESchemeEntryType::Table,
46+
NScheme::ESchemeEntryType::View,
47+
NScheme::ESchemeEntryType::Topic,
48+
NScheme::ESchemeEntryType::CoordinationNode,
49+
NScheme::ESchemeEntryType::Replication,
50+
};
51+
52+
return values;
53+
}
54+
4355
public:
4456
TDbIterator(TDriver driver, const TString& fullPath)
4557
: Client(driver)
@@ -48,7 +60,7 @@ class TDbIterator {
4860
Y_ENSURE(listResult.IsSuccess(), "Can't list directory, maybe it doesn't exist, dbPath# "
4961
<< fullPath.Quote());
5062

51-
if (IsIn({NScheme::ESchemeEntryType::Table, NScheme::ESchemeEntryType::View}, listResult.GetEntry().Type)) {
63+
if (IsIn(SupportedEntryTypes(), listResult.GetEntry().Type)) {
5264
TPathSplitUnix parentPath(fullPath);
5365
parentPath.pop_back();
5466
TraverseRoot = parentPath.Reconstruct();
@@ -145,6 +157,10 @@ class TDbIterator {
145157
return GetCurrentNode()->Type == NScheme::ESchemeEntryType::Directory;
146158
}
147159

160+
bool IsReplication() const {
161+
return GetCurrentNode()->Type == NScheme::ESchemeEntryType::Replication;
162+
}
163+
148164
bool IsListed() const {
149165
return NextNodes.front().IsListed;
150166
}

ydb/public/lib/ydb_cli/dump/files/files.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ enum EFilesType {
1818
CREATE_USER,
1919
CREATE_GROUP,
2020
ALTER_GROUP,
21+
CREATE_ASYNC_REPLICATION,
2122
};
2223

2324
static constexpr TFileInfo FILES_INFO[] = {
@@ -36,6 +37,7 @@ static constexpr TFileInfo FILES_INFO[] = {
3637
{"create_user.sql", "users"},
3738
{"create_group.sql", "groups"},
3839
{"alter_group.sql", "group members"},
40+
{"create_async_replication.sql", "async replication"},
3941
};
4042

4143
const TFileInfo& TableScheme() {
@@ -98,4 +100,8 @@ const TFileInfo& AlterGroup() {
98100
return FILES_INFO[ALTER_GROUP];
99101
}
100102

103+
const TFileInfo& CreateAsyncReplication() {
104+
return FILES_INFO[CREATE_ASYNC_REPLICATION];
105+
}
106+
101107
} // NYdb::NDump::NFiles

ydb/public/lib/ydb_cli/dump/files/files.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ const TFileInfo& Database();
2222
const TFileInfo& CreateUser();
2323
const TFileInfo& CreateGroup();
2424
const TFileInfo& AlterGroup();
25+
const TFileInfo& CreateAsyncReplication();
2526

2627
} // NYdb::NDump:NFiles

0 commit comments

Comments
 (0)