summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Doronin <[email protected]>2026-07-08 21:52:54 +0300
committerGitHub <[email protected]>2026-07-08 18:52:54 +0000
commit673263db5e42fefbd3856dcf321b8814121436ae (patch)
treee1eb563902612b5902f99e1d50bca3a4643f8fc7
parentb95f311913182f013d6f95cfb0a8526cabf49ceb (diff)
backup cancel on propose for cs (#45851)
Co-authored-by: Ilnaz Nizametdinov <[email protected]>
-rw-r--r--ydb/core/tx/schemeshard/schemeshard__operation_backup_restore_common.h33
-rw-r--r--ydb/core/tx/schemeshard/schemeshard_impl.cpp52
-rw-r--r--ydb/core/tx/schemeshard/schemeshard_impl.h1
-rw-r--r--ydb/core/tx/schemeshard/ut_export/ut_export.cpp297
-rw-r--r--ydb/core/tx/schemeshard/ut_export/ya.make3
-rw-r--r--ydb/services/ydb/backup_ut/backup_path_ut.cpp4
6 files changed, 361 insertions, 29 deletions
diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_backup_restore_common.h b/ydb/core/tx/schemeshard/schemeshard__operation_backup_restore_common.h
index 045b6e218c9..b2851baa8f7 100644
--- a/ydb/core/tx/schemeshard/schemeshard__operation_backup_restore_common.h
+++ b/ydb/core/tx/schemeshard/schemeshard__operation_backup_restore_common.h
@@ -73,6 +73,16 @@ public:
Y_ABORT_UNLESS(txState->TxType == TxType);
Y_ABORT_UNLESS(txState->State == TTxState::ConfigureParts);
+ // For column tables, backup propose is async (export actor must finish
+ // before columnshard replies). Handle cancel here to avoid waiting for
+ // that reply which may never come while export is blocked.
+ const TPath path = TPath::Init(txState->TargetPathId, context.SS);
+ if (txState->Cancel && path->IsColumnTable()) {
+ NIceDb::TNiceDb db(context.GetDB());
+ context.SS->ChangeTxState(db, OperationId, TTxState::Aborting);
+ return true;
+ }
+
txState->ClearShardsInProgress();
if constexpr (TKind::NeedSnapshotTime()) {
TKind::ProposeTx(OperationId, *txState, context, GetSnapshotTime(context.SS, txState->TargetPathId));
@@ -106,6 +116,7 @@ public:
IgnoreMessages(DebugHint(),
{ TEvHive::TEvCreateTabletReply::EventType
, TEvDataShard::TEvProposeTransactionResult::EventType
+ , TEvColumnShard::TEvProposeTransactionResult::EventType
, TEvPrivate::TEvOperationPlan::EventType }
);
}
@@ -389,6 +400,7 @@ public:
this->IgnoreMessages(DebugHint(),
{ TEvHive::TEvCreateTabletReply::EventType
, TEvDataShard::TEvProposeTransactionResult::EventType
+ , TEvColumnShard::TEvProposeTransactionResult::EventType
, TEvPrivate::TEvOperationPlan::EventType }
);
}
@@ -463,7 +475,10 @@ public:
: TxType(type)
, OperationId(id)
{
- IgnoreMessages(DebugHint(), {TEvDataShard::TEvProposeTransactionResult::EventType});
+ IgnoreMessages(DebugHint(),
+ { TEvDataShard::TEvProposeTransactionResult::EventType
+ , TEvColumnShard::TEvProposeTransactionResult::EventType }
+ );
}
bool HandleReply(TEvDataShard::TEvSchemaChanged::TPtr& ev, TOperationContext& context) override {
@@ -547,8 +562,22 @@ class TBackupRestoreOperationBase: public TSubOperation {
case TTxState::Waiting:
case TTxState::CreateParts:
return TTxState::ConfigureParts;
- case TTxState::ConfigureParts:
+ case TTxState::ConfigureParts: {
+ const TTxState* txState = context.SS->FindTx(OperationId);
+ Y_ABORT_UNLESS(txState);
+ Y_ABORT_UNLESS(txState->TxType == TxType);
+
+ const TPath path = TPath::Init(txState->TargetPathId, context.SS);
+ if (txState->Cancel && path->IsColumnTable()) {
+ if (txState->State == TTxState::Propose) {
+ return TTxState::Propose;
+ }
+
+ Y_ABORT_UNLESS(txState->State == TTxState::Aborting);
+ return TTxState::Aborting;
+ }
return TTxState::Propose;
+ }
case TTxState::Propose:
return TTxState::ProposedWaitParts;
case TTxState::ProposedWaitParts: {
diff --git a/ydb/core/tx/schemeshard/schemeshard_impl.cpp b/ydb/core/tx/schemeshard/schemeshard_impl.cpp
index 53b4205a734..9efe1205e31 100644
--- a/ydb/core/tx/schemeshard/schemeshard_impl.cpp
+++ b/ydb/core/tx/schemeshard/schemeshard_impl.cpp
@@ -4660,6 +4660,16 @@ void TSchemeShard::PersistColumnTableRemove(NIceDb::TNiceDb& db, TPathId pathId,
UpdateDiskSpaceUsage(db, pathId, TPartitionStats(), tableInfo.GetStats().Aggregated, ctx);
}
+ ClearBackupRestoreHistory(db, pathId, tableInfo.BackupHistory);
+ ClearBackupRestoreHistory(db, pathId, tableInfo.RestoreHistory);
+
+ if (IsLocalId(pathId)) {
+ db.Table<Schema::BackupSettings>().Key(pathId.LocalPathId).Delete();
+ }
+ db.Table<Schema::MigratedBackupSettings>().Key(pathId.OwnerId, pathId.LocalPathId).Delete();
+
+ db.Table<Schema::RestoreTasks>().Key(pathId.OwnerId, pathId.LocalPathId).Delete();
+
db.Table<Schema::ColumnTables>().Key(pathId.LocalPathId).Delete();
ColumnTables.Drop(pathId);
DecrementPathDbRefCount(pathId);
@@ -4901,6 +4911,23 @@ void TSchemeShard::PersistRevertedMigration(NIceDb::TNiceDb& db, TPathId pathId,
db.Table<Schema::RevertedMigrations>().Key(pathId.LocalPathId, abandonedSchemeShardId).Update();
}
+void TSchemeShard::ClearBackupRestoreHistory(NIceDb::TNiceDb& db, TPathId pathId, const TMap<TTxId, TTableInfo::TBackupRestoreResult>& history) {
+ for (const auto& [txId, result] : history) {
+ for (const auto& [shard, _] : result.ShardStatuses) {
+ if (IsLocalId(shard)) {
+ db.Table<Schema::ShardBackupStatus>().Key(txId, shard.GetLocalId()).Delete();
+ }
+ db.Table<Schema::MigratedShardBackupStatus>().Key(txId, shard.GetOwnerId(), shard.GetLocalId()).Delete();
+ db.Table<Schema::TxShardStatus>().Key(txId, shard.GetOwnerId(), shard.GetLocalId()).Delete();
+ }
+
+ if (IsLocalId(pathId)) {
+ db.Table<Schema::CompletedBackups>().Key(pathId.LocalPathId, txId, result.CompletionDateTime).Delete();
+ }
+ db.Table<Schema::MigratedCompletedBackups>().Key(pathId.OwnerId, pathId.LocalPathId, txId, result.CompletionDateTime).Delete();
+ }
+}
+
void TSchemeShard::PersistRemoveTable(NIceDb::TNiceDb& db, TPathId pathId, const TActorContext& ctx)
{
Y_ABORT_UNLESS(PathsById.contains(pathId));
@@ -4911,29 +4938,8 @@ void TSchemeShard::PersistRemoveTable(NIceDb::TNiceDb& db, TPathId pathId, const
}
const TTableInfo::TPtr tableInfo = Tables.at(pathId);
- auto clearHistory = [&](const TMap<TTxId, TTableInfo::TBackupRestoreResult>& history) {
- for (auto& bItem: history) {
- TTxId txId = bItem.first;
- const auto& result = bItem.second;
-
- for (auto& sItem: result.ShardStatuses) {
- auto shard = sItem.first;
- if (IsLocalId(shard)) {
- db.Table<Schema::ShardBackupStatus>().Key(txId, shard.GetLocalId()).Delete();
- }
- db.Table<Schema::MigratedShardBackupStatus>().Key(txId, shard.GetOwnerId(), shard.GetLocalId()).Delete();
- db.Table<Schema::TxShardStatus>().Key(txId, shard.GetOwnerId(), shard.GetLocalId()).Delete();
- }
-
- if (IsLocalId(pathId)) {
- db.Table<Schema::CompletedBackups>().Key(pathId.LocalPathId, txId, result.CompletionDateTime).Delete();
- }
- db.Table<Schema::MigratedCompletedBackups>().Key(pathId.OwnerId, pathId.LocalPathId, txId, result.CompletionDateTime).Delete();
- }
- };
-
- clearHistory(tableInfo->BackupHistory);
- clearHistory(tableInfo->RestoreHistory);
+ ClearBackupRestoreHistory(db, pathId, tableInfo->BackupHistory);
+ ClearBackupRestoreHistory(db, pathId, tableInfo->RestoreHistory);
if (IsLocalId(pathId)) {
db.Table<Schema::BackupSettings>().Key(pathId.LocalPathId).Delete();
diff --git a/ydb/core/tx/schemeshard/schemeshard_impl.h b/ydb/core/tx/schemeshard/schemeshard_impl.h
index 971dec790d1..8d297ab5f63 100644
--- a/ydb/core/tx/schemeshard/schemeshard_impl.h
+++ b/ydb/core/tx/schemeshard/schemeshard_impl.h
@@ -938,6 +938,7 @@ public:
void PersistRemoveKesusInfo(NIceDb::TNiceDb& db, TPathId pathId);
void PersistRemoveTableIndex(NIceDb::TNiceDb& db, TPathId tableId);
void PersistRemoveTable(NIceDb::TNiceDb& db, TPathId tableId, const TActorContext& ctx);
+ void ClearBackupRestoreHistory(NIceDb::TNiceDb& db, TPathId pathId, const TMap<TTxId, TTableInfo::TBackupRestoreResult>& history);
void PersistRevertedMigration(NIceDb::TNiceDb& db, TPathId pathId, TTabletId abandonedSchemeShardId);
void UpdateDiskSpaceUsage(NIceDb::TNiceDb& db, TPathId pathId, const TPartitionStats& newPartitionStats, const TPartitionStats& oldPartitionStats, const TActorContext &ctx);
diff --git a/ydb/core/tx/schemeshard/ut_export/ut_export.cpp b/ydb/core/tx/schemeshard/ut_export/ut_export.cpp
index 905da16f1fc..509539eba70 100644
--- a/ydb/core/tx/schemeshard/ut_export/ut_export.cpp
+++ b/ydb/core/tx/schemeshard/ut_export/ut_export.cpp
@@ -3,6 +3,7 @@
#include <ydb/public/api/protos/ydb_topic.pb.h>
#include <ydb/core/backup/common/encryption.h>
+#include <ydb/core/base/counters.h>
#include <ydb/core/base/table_index.h>
#include <ydb/core/metering/metering.h>
#include <ydb/core/protos/s3_settings.pb.h>
@@ -10,6 +11,8 @@
#include <ydb/core/tablet_flat/shared_cache_events.h>
#include <ydb/core/testlib/actors/block_events.h>
#include <ydb/core/testlib/audit_helpers/audit_helper.h>
+#include <ydb/core/tx/columnshard/columnshard_private_events.h>
+#include <ydb/core/tx/columnshard/test_helper/columnshard_ut_common.h>
#include <ydb/core/tx/datashard/datashard.h>
#include <ydb/core/tx/schemeshard/schemeshard_billing_helpers.h>
#include <ydb/core/tx/schemeshard/ut_helpers/helpers.h>
@@ -18,6 +21,7 @@
#include <ydb/core/wrappers/s3_wrapper.h>
#include <ydb/core/wrappers/ut_helpers/s3_mock.h>
#include <ydb/library/aws_init/aws.h>
+#include <ydb/public/lib/value/value.h>
#include <library/cpp/testing/hook/hook.h>
@@ -1164,6 +1168,188 @@ namespace {
Env().TestWaitNotification(Runtime(), exportTxId);
}
+ struct TColumnTableInfo {
+ ui64 PathId = 0;
+ ui64 ShardId = 0;
+ };
+
+ struct TColumnTableSlowS3ExportContext {
+ ui64 TxId = 0;
+ ui64 ExportId = 0;
+ ui64 ShardId = 0;
+ ui64 PathId = 0;
+ THolder<NActors::TBlockEvents<NKikimr::NColumnShard::TEvPrivate::TEvBackupExportRecordBatch>> BlockExportBatch;
+ std::function<ui64()> GetAliveCounter;
+ };
+
+ void InitColumnTableExportSlowS3Test(bool enableScanWriteLog = true) {
+ Env();
+ Runtime().GetAppData().FeatureFlags.SetEnableColumnTablesBackup(true);
+ Runtime().SetLogPriority(NKikimrServices::TX_COLUMNSHARD, NActors::NLog::PRI_DEBUG);
+ if (enableScanWriteLog) {
+ Runtime().SetLogPriority(NKikimrServices::TX_COLUMNSHARD_SCAN, NActors::NLog::PRI_DEBUG);
+ Runtime().SetLogPriority(NKikimrServices::TX_COLUMNSHARD_WRITE, NActors::NLog::PRI_DEBUG);
+ }
+ }
+
+ std::function<ui64()> MakeColumnTableExportAliveCounter() {
+ return [this]() {
+ auto subgroup = GetServiceCounters(Runtime().GetDynamicCounters(0), "tablets")
+ ->GetSubgroup("subsystem", "columnshard")
+ ->GetSubgroup("module_id", "ExportActor");
+ return subgroup->GetCounter("Value/Export/Actors/Alive", false)->Val();
+ };
+ }
+
+ TColumnTableInfo CreateColumnTableWithData(ui64& txId, const TString& tableName) {
+ TestCreateColumnTable(Runtime(), ++txId, "/MyRoot", Sprintf(R"(
+ Name: "%s"
+ ColumnShardCount: 1
+ Schema {
+ Columns { Name: "timestamp" Type: "Timestamp" NotNull: true }
+ Columns { Name: "value" Type: "Utf8" }
+ KeyColumnNames: "timestamp"
+ }
+ )", tableName.c_str()));
+ Env().TestWaitNotification(Runtime(), txId);
+
+ TColumnTableInfo info;
+ {
+ auto describe = DescribePath(Runtime(), Sprintf("/MyRoot/%s", tableName.c_str()));
+ TestDescribeResult(describe, {NLs::PathExist});
+ info.PathId = describe.GetPathId();
+ const auto& sharding = describe.GetPathDescription().GetColumnTableDescription().GetSharding();
+ UNIT_ASSERT_VALUES_EQUAL(sharding.ColumnShardsSize(), 1);
+ info.ShardId = sharding.GetColumnShards()[0];
+ }
+ UNIT_ASSERT(info.ShardId);
+ UNIT_ASSERT(info.PathId);
+
+ {
+ TActorId sender = Runtime().AllocateEdgeActor();
+ const std::vector<NArrow::NTest::TTestColumn> ydbSchema = {
+ NArrow::NTest::TTestColumn("timestamp", NScheme::TTypeInfo(NScheme::NTypeIds::Timestamp)).SetNullable(false),
+ NArrow::NTest::TTestColumn("value", NScheme::TTypeInfo(NScheme::NTypeIds::Utf8)),
+ };
+ const auto data = NTxUT::MakeTestBlob({0, 100}, ydbSchema, {}, {"timestamp"});
+ ui64 writeId = 0;
+ std::vector<ui64> writeIds;
+ ++txId;
+ NTxUT::WriteData(Runtime(), sender, info.ShardId, ++writeId, info.PathId, data, ydbSchema, &writeIds, NEvWrite::EModificationType::Upsert, txId);
+ auto planStep = NTxUT::ProposeCommit(Runtime(), sender, info.ShardId, txId, writeIds, txId);
+ NTxUT::PlanCommit(Runtime(), sender, info.ShardId, planStep, {txId});
+ }
+
+ return info;
+ }
+
+ ui64 StartColumnTableS3Export(ui64& txId, const TString& tableName, const TString& destinationPrefix) {
+ const ui64 exportTxId = ++txId;
+ TestExport(Runtime(), exportTxId, "/MyRoot", Sprintf(R"(
+ ExportToS3Settings {
+ endpoint: "localhost:%d"
+ scheme: HTTP
+ items {
+ source_path: "/MyRoot/%s"
+ destination_prefix: "%s"
+ }
+ }
+ )", S3Port(), tableName.c_str(), destinationPrefix.c_str()));
+ return exportTxId;
+ }
+
+ TColumnTableSlowS3ExportContext SetupColumnTableSlowS3Export(
+ ui64 startTxId = 100,
+ const TString& tableName = "ColumnTable",
+ const TString& destinationPrefix = "",
+ bool enableScanWriteLog = true)
+ {
+ InitColumnTableExportSlowS3Test(enableScanWriteLog);
+
+ TColumnTableSlowS3ExportContext ctx;
+ ctx.TxId = startTxId;
+
+ const auto tableInfo = CreateColumnTableWithData(ctx.TxId, tableName);
+ ctx.PathId = tableInfo.PathId;
+ ctx.ShardId = tableInfo.ShardId;
+
+ ctx.BlockExportBatch = MakeHolder<NActors::TBlockEvents<NKikimr::NColumnShard::TEvPrivate::TEvBackupExportRecordBatch>>(Runtime());
+ ctx.ExportId = StartColumnTableS3Export(ctx.TxId, tableName, destinationPrefix);
+
+ ctx.GetAliveCounter = MakeColumnTableExportAliveCounter();
+ Runtime().WaitFor("export actor alive", [&]{ return ctx.GetAliveCounter() != 0; }, TDuration::Seconds(60));
+
+ return ctx;
+ }
+
+ void VerifyExportCancelledAndForget(ui64& txId, ui64 exportId) {
+ Env().TestWaitNotification(Runtime(), exportId);
+
+ auto desc = TestGetExport(Runtime(), exportId, "/MyRoot", Ydb::StatusIds::CANCELLED);
+ auto entry = desc.GetResponse().GetEntry();
+ UNIT_ASSERT_VALUES_EQUAL(entry.GetProgress(), Ydb::Export::ExportProgress::PROGRESS_CANCELLED);
+
+ TestForgetExport(Runtime(), ++txId, "/MyRoot", exportId);
+ Env().TestWaitNotification(Runtime(), exportId);
+
+ TestGetExport(Runtime(), exportId, "/MyRoot", Ydb::StatusIds::NOT_FOUND);
+ }
+
+ void FinalizeColumnTableSlowS3Export(TColumnTableSlowS3ExportContext& ctx) {
+ Runtime().WaitFor("export actors stopped", [&]{ return ctx.GetAliveCounter() == 0; }, TDuration::Seconds(60));
+ ctx.BlockExportBatch->Stop();
+ ctx.BlockExportBatch->Unblock();
+ }
+
+ ui64 CountCompletedBackupsRows() {
+ const auto result = LocalMiniKQL(Runtime(), TTestTxConfig::SchemeShard, R"(
+ (
+ (let range '(
+ '('PathId (Null) (Void))
+ '('TxId (Null) (Void))
+ '('DateTimeOfCompletion (Null) (Void))
+ ))
+ (let fields '('PathId))
+ (return (AsList
+ (SetResult 'Result (SelectRange 'CompletedBackups range fields '()))
+ ))
+ )
+ )");
+ return NKikimr::NClient::TValue::Create(result)[0]["List"].Size();
+ }
+
+ void CancelColumnTableExportWithReboot(bool rebootCS, bool rebootSS, bool rebootBeforeCancel) {
+ auto ctx = SetupColumnTableSlowS3Export();
+ ui64 txId = ctx.TxId;
+ const ui64 exportId = ctx.ExportId;
+ const ui64 shardId = ctx.ShardId;
+
+ if (rebootBeforeCancel) {
+ if (rebootCS) {
+ RebootTablet(Runtime(), shardId, Runtime().AllocateEdgeActor());
+ }
+ if (rebootSS) {
+ RebootTablet(Runtime(), TTestTxConfig::SchemeShard, Runtime().AllocateEdgeActor());
+ }
+ }
+
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", exportId);
+
+ if (!rebootBeforeCancel) {
+ if (rebootCS) {
+ RebootTablet(Runtime(), shardId, Runtime().AllocateEdgeActor());
+ }
+ if (rebootSS) {
+ RebootTablet(Runtime(), TTestTxConfig::SchemeShard, Runtime().AllocateEdgeActor());
+ }
+ }
+
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", exportId);
+
+ VerifyExportCancelledAndForget(txId, exportId);
+ FinalizeColumnTableSlowS3Export(ctx);
+ }
+
private:
TPortManager PortManager;
ui16 S3ServerPort = 0;
@@ -4790,4 +4976,115 @@ CREATE EXTERNAL TABLE IF NOT EXISTS `ExternalTable` (
const TString decrypted(decryptedData.Data(), decryptedData.Size());
UNIT_ASSERT_VALUES_EQUAL_C(decrypted, expected, "Encrypted buffer corruption detected");
}
+
+ Y_UNIT_TEST(CancelColumnTableExportDuringSlowS3ViaSSPipeline) {
+ auto ctx = SetupColumnTableSlowS3Export();
+ ui64 txId = ctx.TxId;
+
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", ctx.ExportId);
+
+ VerifyExportCancelledAndForget(txId, ctx.ExportId);
+ FinalizeColumnTableSlowS3Export(ctx);
+ }
+
+ Y_UNIT_TEST(MultipleCancelsColumnTableExportDuringSlowS3ViaSSPipeline) {
+ auto ctx = SetupColumnTableSlowS3Export();
+ ui64 txId = ctx.TxId;
+
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", ctx.ExportId);
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", ctx.ExportId);
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", ctx.ExportId);
+
+ VerifyExportCancelledAndForget(txId, ctx.ExportId);
+ FinalizeColumnTableSlowS3Export(ctx);
+ }
+
+ Y_UNIT_TEST(CancelColumnTableExportRebootCSThenCancelAgain) {
+ CancelColumnTableExportWithReboot(/*rebootCS=*/true, /*rebootSS=*/false, /*rebootBeforeCancel=*/false);
+ }
+
+ Y_UNIT_TEST(CancelColumnTableExportRebootSSThenCancelAgain) {
+ CancelColumnTableExportWithReboot(/*rebootCS=*/false, /*rebootSS=*/true, /*rebootBeforeCancel=*/false);
+ }
+
+ Y_UNIT_TEST(CancelColumnTableExportRebootBothThenCancelAgain) {
+ CancelColumnTableExportWithReboot(/*rebootCS=*/true, /*rebootSS=*/true, /*rebootBeforeCancel=*/false);
+ }
+
+ Y_UNIT_TEST(RebootCSThenCancelColumnTableExportTwice) {
+ CancelColumnTableExportWithReboot(/*rebootCS=*/true, /*rebootSS=*/false, /*rebootBeforeCancel=*/true);
+ }
+
+ Y_UNIT_TEST(RebootSSThenCancelColumnTableExportTwice) {
+ CancelColumnTableExportWithReboot(/*rebootCS=*/false, /*rebootSS=*/true, /*rebootBeforeCancel=*/true);
+ }
+
+ Y_UNIT_TEST(RebootBothThenCancelColumnTableExportTwice) {
+ CancelColumnTableExportWithReboot(/*rebootCS=*/true, /*rebootSS=*/true, /*rebootBeforeCancel=*/true);
+ }
+
+ Y_UNIT_TEST(CancelTwoColumnTableExportsDuringSlowS3ViaSSPipeline) {
+ InitColumnTableExportSlowS3Test();
+ ui64 txId = 100;
+
+ for (const auto& tableName : {"ColumnTable1", "ColumnTable2"}) {
+ CreateColumnTableWithData(txId, tableName);
+ }
+
+ TColumnTableSlowS3ExportContext ctx;
+ ctx.BlockExportBatch = MakeHolder<NActors::TBlockEvents<NKikimr::NColumnShard::TEvPrivate::TEvBackupExportRecordBatch>>(Runtime());
+ ctx.GetAliveCounter = MakeColumnTableExportAliveCounter();
+
+ const ui64 exportTxId1 = StartColumnTableS3Export(txId, "ColumnTable1", "table1");
+ const ui64 exportTxId2 = StartColumnTableS3Export(txId, "ColumnTable2", "table2");
+
+ Runtime().WaitFor("export actors alive", [&]{ return ctx.GetAliveCounter() >= 2; }, TDuration::Seconds(60));
+
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", exportTxId1);
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", exportTxId2);
+
+ VerifyExportCancelledAndForget(txId, exportTxId1);
+ VerifyExportCancelledAndForget(txId, exportTxId2);
+ FinalizeColumnTableSlowS3Export(ctx);
+ }
+
+ Y_UNIT_TEST(CancelColumnTableExportThenForgetThenReboot) {
+ auto ctx = SetupColumnTableSlowS3Export(/*startTxId=*/100, "ColumnTable", "", /*enableScanWriteLog=*/false);
+ ui64 txId = ctx.TxId;
+
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", ctx.ExportId);
+ TestCancelExport(Runtime(), ++txId, "/MyRoot", ctx.ExportId);
+
+ VerifyExportCancelledAndForget(txId, ctx.ExportId);
+ FinalizeColumnTableSlowS3Export(ctx);
+
+ RebootTablet(Runtime(), TTestTxConfig::SchemeShard, Runtime().AllocateEdgeActor());
+ }
+
+ Y_UNIT_TEST(DropColumnTableAfterCompletedExportClearsBackupHistory) {
+ InitColumnTableExportSlowS3Test();
+ Runtime().GetAppData().FeatureFlags.SetEnableExportAutoDropping(false);
+
+ ui64 txId = 100;
+ CreateColumnTableWithData(txId, "ColumnTable");
+
+ const ui64 exportId = StartColumnTableS3Export(txId, "ColumnTable", "dest");
+ Env().TestWaitNotification(Runtime(), exportId);
+ TestGetExport(Runtime(), exportId, "/MyRoot", Ydb::StatusIds::SUCCESS);
+
+ const TString exportCopyPath = Sprintf("/MyRoot/export-%" PRIu64 "/0", exportId);
+ TestDescribeResult(DescribePath(Runtime(), exportCopyPath), {NLs::PathExist});
+ UNIT_ASSERT_VALUES_UNEQUAL(CountCompletedBackupsRows(), 0u);
+
+ TestDropColumnTable(Runtime(), ++txId, Sprintf("/MyRoot/export-%" PRIu64, exportId), "0");
+ Env().TestWaitNotification(Runtime(), txId);
+
+ UNIT_ASSERT_VALUES_EQUAL(CountCompletedBackupsRows(), 0u);
+ TestDescribeResult(DescribePath(Runtime(), exportCopyPath), {NLs::PathNotExist});
+
+ RebootTablet(Runtime(), TTestTxConfig::SchemeShard, Runtime().AllocateEdgeActor());
+
+ TestDescribeResult(DescribePath(Runtime(), "/MyRoot/ColumnTable"), {NLs::PathExist});
+ UNIT_ASSERT_VALUES_EQUAL(CountCompletedBackupsRows(), 0u);
+ }
}
diff --git a/ydb/core/tx/schemeshard/ut_export/ya.make b/ydb/core/tx/schemeshard/ut_export/ya.make
index 0d5ed984ac2..b5ea6b42b29 100644
--- a/ydb/core/tx/schemeshard/ut_export/ya.make
+++ b/ydb/core/tx/schemeshard/ut_export/ya.make
@@ -20,6 +20,9 @@ IF (NOT OS_WINDOWS)
library/cpp/svnversion
ydb/core/testlib/default
ydb/core/tx
+ ydb/core/tx/columnshard
+ ydb/core/tx/columnshard/hooks/testing
+ ydb/core/tx/columnshard/test_helper
ydb/core/tx/schemeshard/ut_helpers
ydb/core/util
ydb/core/wrappers/ut_helpers
diff --git a/ydb/services/ydb/backup_ut/backup_path_ut.cpp b/ydb/services/ydb/backup_ut/backup_path_ut.cpp
index c137ec424ef..0297a6ff37a 100644
--- a/ydb/services/ydb/backup_ut/backup_path_ut.cpp
+++ b/ydb/services/ydb/backup_ut/backup_path_ut.cpp
@@ -1661,10 +1661,6 @@ void CancelWhileProcessingImpl(TBackupTestFixture& f, bool isOlap) {
f.Server().GetRuntime()->GetAppData().FeatureFlags.SetEnableFsBackups(true);
- if (isOlap) {
- return;
- }
-
auto createSchemaResult = f.YdbQueryClient().ExecuteQuery(fmt::format(R"sql(
CREATE TABLE `/Root/Table0` (
key Uint32 NOT NULL,