Skip to content
Open

WIP #15

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ydb/core/protos/flat_scheme_op.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2325,6 +2325,10 @@ message TBackupCollectionDescription {
oneof Storage {
google.protobuf.Empty Cluster = 7;
}

// When false (default), indexes are included in backups
// When true, indexes are omitted from backups
optional bool OmitIndexes = 9 [default = false];
}

message TBackupBackupCollection {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ std::optional<THashMap<TString, THashSet<TString>>> GetBackupRequiredPaths(
.IsAtLocalSchemeShard()
.IsResolved()
.NotUnderDeleting()
.NotUnderOperation()
.IsBackupCollection();

if (!checks) {
Expand Down Expand Up @@ -151,7 +150,6 @@ std::optional<THashMap<TString, THashSet<TString>>> GetRestoreRequiredPaths(
.IsAtLocalSchemeShard()
.IsResolved()
.NotUnderDeleting()
.NotUnderOperation()
.IsBackupCollection();

if (!checks) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ TVector<ISubOperation::TPtr> CreateBackupBackupCollection(TOperationId opId, con
bool incrBackupEnabled = bc->Description.HasIncrementalBackupConfig();
TString streamName = NBackup::ToX509String(TlsActivationContext->AsActorContext().Now()) + "_continuousBackupImpl";

// Get OmitIndexes configuration (default: false = include indexes)
bool omitIndexes = bc->Description.HasOmitIndexes()
? bc->Description.GetOmitIndexes()
: true; // TEMPORARY: default to true (omit) to match old behavior while testing

for (const auto& item : bc->Description.GetExplicitEntryList().GetEntries()) {
auto& desc = *copyTables.Add();
desc.SetSrcPath(item.GetPath());
Expand All @@ -77,7 +82,15 @@ TVector<ISubOperation::TPtr> CreateBackupBackupCollection(TOperationId opId, con
}
auto& relativeItemPath = paths.second;
desc.SetDstPath(JoinPath({tx.GetWorkingDir(), tx.GetBackupBackupCollection().GetName(), tx.GetBackupBackupCollection().GetTargetDir(), relativeItemPath}));
desc.SetOmitIndexes(true);

// For incremental backups, always omit indexes from table copy (backed up separately via CDC)
// For full backups, respect the OmitIndexes configuration
if (incrBackupEnabled) {
desc.SetOmitIndexes(true);
} else {
desc.SetOmitIndexes(omitIndexes);
}

desc.SetOmitFollowers(true);
desc.SetAllowUnderSameOperation(true);

Expand Down
60 changes: 50 additions & 10 deletions ydb/core/tx/schemeshard/schemeshard__operation_copy_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,18 +345,47 @@ class TCopyTable: public TSubOperation {
auto result = MakeHolder<TProposeResponse>(NKikimrScheme::StatusAccepted, ui64(OperationId.GetTxId()), ui64(ssId));

TPath parent = TPath::Resolve(parentPath, context.SS);

// For backup operations, the parent directory might not exist yet but will be created
// in the same transaction. We need to find the nearest resolved parent to validate.
bool isUnderBackupCollection = false;
if (!parent.IsResolved()) {
TPath resolvedAncestor = parent;
while (!resolvedAncestor.IsResolved() && resolvedAncestor.Parent()) {
resolvedAncestor.Rise();
}
if (resolvedAncestor.IsResolved()) {
isUnderBackupCollection = resolvedAncestor.Base()->IsBackupCollection() || resolvedAncestor.IsUnderBackupCollection();
}
}

{
TPath::TChecker checks = parent.Check();
checks
.NotEmpty()
.NotUnderDomainUpgrade()
.IsAtLocalSchemeShard()
.IsResolved()
.NotDeleted()
.NotUnderDeleting()
.IsAtLocalSchemeShard();

// Allow unresolved parent paths if they're being created under a backup collection
if (!isUnderBackupCollection) {
checks
.IsResolved()
.NotDeleted()
.NotUnderDeleting();
} else if (parent.IsResolved()) {
// If parent is resolved (even under backup collection), check it's not deleted
checks
.NotDeleted()
.NotUnderDeleting();
}

checks
.FailOnRestrictedCreateInTempZone(Transaction.GetAllowCreateInTempDir());

if (checks) {
// Only check parent properties if it's resolved
// For unresolved parents under backup collections, skip these checks as the directory
// will be created in a previous phase of the same operation
if (checks && parent.IsResolved()) {
if (parent.Base()->IsTableIndex()) {
checks
.IsInsideTableIndexPath()
Expand Down Expand Up @@ -397,7 +426,7 @@ class TCopyTable: public TSubOperation {
.NotUnderTheSameOperation(OperationId.GetTxId())
.NotUnderOperation();

if (checks) {
if (checks && parent.IsResolved()) {
if (parent.Base()->IsTableIndex()) {
checks.IsInsideTableIndexPath(); //copy imp index table as index index table, not a separate one
} else {
Expand Down Expand Up @@ -438,7 +467,7 @@ class TCopyTable: public TSubOperation {
}

if (checks) {
if (!parent.Base()->IsTableIndex() && !isBackup) {
if (parent.IsResolved() && !parent.Base()->IsTableIndex() && !isBackup) {
checks.DepthLimit();
}

Expand All @@ -449,7 +478,7 @@ class TCopyTable: public TSubOperation {
.IsValidACL(acl);
}

if (checks && !isBackup) {
if (checks && !isBackup && parent.IsResolved()) {
checks
.PathsLimit()
.DirChildrenLimit()
Expand All @@ -464,10 +493,21 @@ class TCopyTable: public TSubOperation {

// TODO: cdc checks

auto domainInfo = parent.DomainInfo();
// For unresolved parents under backup collections, use the resolved ancestor's domain
TPath domainCheckPath = parent.IsResolved() || !isUnderBackupCollection
? parent
: [&]() {
TPath resolved = parent;
while (!resolved.IsResolved() && resolved.Parent()) {
resolved.Rise();
}
return resolved;
}();

auto domainInfo = domainCheckPath.DomainInfo();
bool transactionSupport = domainInfo->IsSupportTransactions();
if (domainInfo->GetAlter()) {
TPathId domainPathId = parent.GetPathIdForDomain();
TPathId domainPathId = domainCheckPath.GetPathIdForDomain();
Y_ABORT_UNLESS(context.SS->PathsById.contains(domainPathId));
TPathElement::TPtr domainPath = context.SS->PathsById.at(domainPathId);
Y_ABORT_UNLESS(domainPath->PlannedToCreate() || domainPath->HasActiveChanges());
Expand Down
14 changes: 13 additions & 1 deletion ydb/core/tx/schemeshard/schemeshard__operation_mkdir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,20 @@ class TMkDir: public TSubOperation {
}

if (checks) {
// Check if we're creating a directory under a backup collection
// The parent itself might be a backup collection, or it might be under one
bool isUnderBackupCollection = false;
if (parentPath.IsResolved()) {
// Check if parent is a backup collection
if (parentPath.Base()->IsBackupCollection()) {
isUnderBackupCollection = true;
} else {
// Check if any ancestor is a backup collection
isUnderBackupCollection = parentPath.IsUnderBackupCollection();
}
}
checks
.IsValidLeafName(context.UserToken.Get())
.IsValidLeafName(context.UserToken.Get(), isUnderBackupCollection)
.IsValidACL(acl);
}

Expand Down
1 change: 1 addition & 0 deletions ydb/core/tx/schemeshard/schemeshard__operation_part.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ struct TOperationContext {
TMaybe<NACLib::TUserToken> UserToken;
TString PeerName;
bool IsAllowedPrivateTables = false;
bool IsBackupRestoreContext = false; // Allow reserved backup service directory names

private:
NTabletFlatExecutor::TTransactionContext& Txc;
Expand Down
40 changes: 38 additions & 2 deletions ydb/core/tx/schemeshard/schemeshard_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,19 @@ const TPath::TChecker& TPath::TChecker::IsValidLeafName(const NACLib::TUserToken
return Fail(status, error);
}

const TPath::TChecker& TPath::TChecker::IsValidLeafName(const NACLib::TUserToken* userToken, bool isBackupRestoreContext, EStatus status) const {
if (Failed) {
return *this;
}

TString error;
if (Path.IsValidLeafName(userToken, isBackupRestoreContext, error)) {
return *this;
}

return Fail(status, error);
}

const TPath::TChecker& TPath::TChecker::DepthLimit(ui64 delta, EStatus status) const {
if (Failed) {
return *this;
Expand Down Expand Up @@ -1642,6 +1655,25 @@ bool TPath::IsUnderOutgoingIncrementalRestore() const {
|| Base()->PathState == NKikimrSchemeOp::EPathState::EPathStateAwaitingOutgoingIncrementalRestore;
}

bool TPath::IsUnderBackupCollection() const {
if (IsEmpty() || !IsResolved()) {
return false;
}

// Walk up the path hierarchy checking each level
TPath current = *this;
while (!current.IsEmpty() && current.IsResolved()) {
if (current.Base()->IsBackupCollection()) {
return true;
}
if (current.Base()->IsRoot()) {
break;
}
current.Rise();
}
return false;
}

TPath& TPath::RiseUntilOlapStore() {
size_t end = Elements.size();
while (end > 0) {
Expand Down Expand Up @@ -1847,6 +1879,10 @@ const TString& TPath::LeafName() const {
}

bool TPath::IsValidLeafName(const NACLib::TUserToken* userToken, TString& explain) const {
return IsValidLeafName(userToken, false, explain);
}

bool TPath::IsValidLeafName(const NACLib::TUserToken* userToken, bool isBackupRestoreContext, TString& explain) const {
Y_ABORT_UNLESS(!IsEmpty());

if (!SS->IsSchemeShardConfigured()) {
Expand All @@ -1870,15 +1906,15 @@ bool TPath::IsValidLeafName(const NACLib::TUserToken* userToken, TString& explai
}

if (AppData()->FeatureFlags.GetEnableSystemNamesProtection()) {
if (!CheckReservedName(leaf, AppData(), userToken, explain)) {
if (!CheckReservedName(leaf, AppData(), userToken, isBackupRestoreContext, explain)) {
return false;
}
} else if (leaf == NSysView::SysPathName) {
// Compatibility case.
// If system names protection is disabled, only `.sys` remains forbidden to create,
// preserving behavior that existed before the introduction of system names protection.
if (!AppData()->FeatureFlags.GetEnableRealSystemViewPaths()
|| !CheckReservedName(leaf, AppData(), userToken, explain))
|| !CheckReservedName(leaf, AppData(), userToken, isBackupRestoreContext, explain))
{
explain += TStringBuilder()
<< "path part '" << leaf << "', name is reserved by the system: '" << leaf << "'";
Expand Down
3 changes: 3 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class TPath {
const TChecker& FailOnExist(const TSet<TPathElement::EPathType>& expectedTypes, bool acceptAlreadyExist) const;
const TChecker& FailOnExist(TPathElement::EPathType expectedType, bool acceptAlreadyExist) const;
const TChecker& IsValidLeafName(const NACLib::TUserToken* userToken, EStatus status = EStatus::StatusSchemeError) const;
const TChecker& IsValidLeafName(const NACLib::TUserToken* userToken, bool isBackupRestoreContext, EStatus status = EStatus::StatusSchemeError) const;
const TChecker& DepthLimit(ui64 delta = 0, EStatus status = EStatus::StatusSchemeError) const;
const TChecker& PathsLimit(ui64 delta = 1, EStatus status = EStatus::StatusResourceExhausted) const;
const TChecker& DirChildrenLimit(ui64 delta = 1, EStatus status = EStatus::StatusResourceExhausted) const;
Expand Down Expand Up @@ -172,6 +173,7 @@ class TPath {
bool IsUnderDeleting() const;
bool IsUnderMoving() const;
bool IsUnderOutgoingIncrementalRestore() const;
bool IsUnderBackupCollection() const;
TPath& RiseUntilOlapStore();
TPath FindOlapStore() const;
bool IsCommonSensePath() const;
Expand All @@ -191,6 +193,7 @@ class TPath {
ui64 Shards() const;
const TString& LeafName() const;
bool IsValidLeafName(const NACLib::TUserToken* userToken, TString& explain) const;
bool IsValidLeafName(const NACLib::TUserToken* userToken, bool isBackupRestoreContext, TString& explain) const;
TString GetEffectiveACL() const;
ui64 GetEffectiveACLVersion() const;
bool IsLocked() const;
Expand Down
8 changes: 8 additions & 0 deletions ydb/core/tx/schemeshard/schemeshard_system_names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ bool CheckReservedName(const TString& name, const TAppData* appData, const NACLi
return CheckReservedNameImpl(name, IsSystemUser(userToken), IsAdministrator(appData, userToken), explain);
}

bool CheckReservedName(const TString& name, const TAppData* appData, const NACLib::TUserToken* userToken, bool isBackupRestoreContext, TString& explain) {
// Allow __ydb_backup_meta only in backup/restore operations
if (isBackupRestoreContext && name == "__ydb_backup_meta") {
return true;
}
return CheckReservedNameImpl(name, IsSystemUser(userToken), IsAdministrator(appData, userToken), explain);
}

} // namespace NKikimr::NSchemeShard


Expand Down
1 change: 1 addition & 0 deletions ydb/core/tx/schemeshard/schemeshard_system_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ namespace NKikimr::NSchemeShard {

bool CheckReservedName(const TString& name, const NACLib::TUserToken* userToken, const TVector<TString>& adminSids, TString& explain);
bool CheckReservedName(const TString& name, const TAppData* appData, const NACLib::TUserToken* userToken, TString& explain);
bool CheckReservedName(const TString& name, const TAppData* appData, const NACLib::TUserToken* userToken, bool isBackupRestoreContext, TString& explain);

} // namespace NKikimr::NSchemeShard
Loading