diff --git a/ydb/core/tx/schemeshard/schemeshard__operation_alter_pq.cpp b/ydb/core/tx/schemeshard/schemeshard__operation_alter_pq.cpp index 07be59c52872..e5fcbab7a8e9 100644 --- a/ydb/core/tx/schemeshard/schemeshard__operation_alter_pq.cpp +++ b/ydb/core/tx/schemeshard/schemeshard__operation_alter_pq.cpp @@ -792,11 +792,13 @@ class TAlterPQ: public TSubOperation { return result; } + const auto& stats = topic->Stats; const PQGroupReserve reserve(newTabletConfig, alterData->ActivePartitionCount); const PQGroupReserve reserveForCheckLimit(newTabletConfig, alterData->ActivePartitionCount + involvedPartitions.size()); const PQGroupReserve oldReserve(tabletConfig, topic->ActivePartitionCount); + const PQGroupReserve oldReserveForCheckLimit(tabletConfig, topic->ActivePartitionCount, stats.DataSize); - const ui64 storageToReserve = reserveForCheckLimit.Storage > oldReserve.Storage ? reserveForCheckLimit.Storage - oldReserve.Storage : 0; + const ui64 storageToReserve = reserveForCheckLimit.Storage > oldReserveForCheckLimit.Storage ? reserveForCheckLimit.Storage - oldReserveForCheckLimit.Storage : 0; { TPath::TChecker checks = path.Check(); diff --git a/ydb/core/tx/schemeshard/schemeshard_utils.cpp b/ydb/core/tx/schemeshard/schemeshard_utils.cpp index d6502afbab93..8d5d3f68b606 100644 --- a/ydb/core/tx/schemeshard/schemeshard_utils.cpp +++ b/ydb/core/tx/schemeshard/schemeshard_utils.cpp @@ -8,8 +8,9 @@ namespace NKikimr { namespace NSchemeShard { -PQGroupReserve::PQGroupReserve(const ::NKikimrPQ::TPQTabletConfig& tabletConfig, ui64 partitions) { - Storage = partitions * NPQ::TopicPartitionReserveSize(tabletConfig); +PQGroupReserve::PQGroupReserve(const ::NKikimrPQ::TPQTabletConfig& tabletConfig, ui64 partitions, ui64 currentStorageUsage) { + Storage = NKikimrPQ::TPQTabletConfig::METERING_MODE_REQUEST_UNITS == tabletConfig.GetMeteringMode() + ? currentStorageUsage : partitions * NPQ::TopicPartitionReserveSize(tabletConfig); Throughput = partitions * NPQ::TopicPartitionReserveThroughput(tabletConfig); } diff --git a/ydb/core/tx/schemeshard/schemeshard_utils.h b/ydb/core/tx/schemeshard/schemeshard_utils.h index 729764c61e41..3283610ba03f 100644 --- a/ydb/core/tx/schemeshard/schemeshard_utils.h +++ b/ydb/core/tx/schemeshard/schemeshard_utils.h @@ -38,7 +38,7 @@ inline NKikimrSchemeOp::TModifyScheme TransactionTemplate(const TString& working class PQGroupReserve { public: - PQGroupReserve(const ::NKikimrPQ::TPQTabletConfig& tabletConfig, ui64 partitions); + PQGroupReserve(const ::NKikimrPQ::TPQTabletConfig& tabletConfig, ui64 partitions, ui64 currentStorageUsage = 0); ui64 Storage; ui64 Throughput; diff --git a/ydb/core/tx/schemeshard/ut_base/ut_base.cpp b/ydb/core/tx/schemeshard/ut_base/ut_base.cpp index 79651dfa906f..3ee4372e24cf 100644 --- a/ydb/core/tx/schemeshard/ut_base/ut_base.cpp +++ b/ydb/core/tx/schemeshard/ut_base/ut_base.cpp @@ -11595,6 +11595,88 @@ Y_UNIT_TEST_SUITE(TSchemeShardTest) { env.TestWaitNotification(runtime, txId); } + Y_UNIT_TEST(AlterTopicOverDiskSpaceQuotas) { + TTestBasicRuntime runtime; + + TTestEnvOptions opts; + opts.DisableStatsBatching(true); + opts.EnablePersistentPartitionStats(true); + opts.EnableTopicDiskSubDomainQuota(true); + + TTestEnv env(runtime, opts); + + ui64 txId = 100; + + // Subdomain with a 1-byte data size quota + TestCreateSubDomain(runtime, ++txId, "/MyRoot", R"( + Name: "USER_1" + PlanResolution: 50 + Coordinators: 1 + Mediators: 1 + TimeCastBucketsPerMediator: 2 + StoragePools { + Name: "name_USER_0_kind_hdd-1" + Kind: "hdd-1" + } + StoragePools { + Name: "name_USER_0_kind_hdd-2" + Kind: "hdd-2" + } + DatabaseQuotas { + data_size_hard_quota: 100 + } + )"); + env.TestWaitNotification(runtime, txId); + + TestCreatePQGroup(runtime, ++txId, "/MyRoot/USER_1", R"( + Name: "Topic1" + TotalGroupCount: 1 + PartitionPerTablet: 1 + PQTabletConfig { + PartitionConfig { + LifetimeSeconds: 1 + WriteSpeedInBytesPerSecond : 100 + } + MeteringMode: METERING_MODE_REQUEST_UNITS + } + )"); + env.TestWaitNotification(runtime, txId); + + ui64 topicId = DescribePath(runtime, "/MyRoot/USER_1/Topic1").GetPathDescription().GetSelf().GetPathId(); + + ui64 generation = 1; + ui64 round = 1; + + // Now topic use 50 bytes of the storage + SendTEvPeriodicTopicStats(runtime, topicId, generation, ++round, 50, 0); + + // Now we reserve 150 bytes but limit 100 bytes + TestAlterPQGroup(runtime, ++txId, "/MyRoot/USER_1", R"( + Name: "Topic1" + PQTabletConfig { + PartitionConfig { + LifetimeSeconds: 1 + WriteSpeedInBytesPerSecond : 150 + } + MeteringMode: METERING_MODE_RESERVED_CAPACITY + } + )", {{TEvSchemeShard::EStatus::StatusResourceExhausted, "database size limit exceeded"}}); + env.TestWaitNotification(runtime, txId); + + // Now we reserve 100 bytes and limit 100 bytes + TestAlterPQGroup(runtime, ++txId, "/MyRoot/USER_1", R"( + Name: "Topic1" + PQTabletConfig { + PartitionConfig { + LifetimeSeconds: 1 + WriteSpeedInBytesPerSecond : 100 + } + MeteringMode: METERING_MODE_RESERVED_CAPACITY + } + )"); + env.TestWaitNotification(runtime, txId); + } + Y_UNIT_TEST(CreateSystemColumn) { TTestBasicRuntime runtime; TTestEnv env(runtime);