|
16 | 16 | #include <library/cpp/testing/unittest/registar.h> |
17 | 17 |
|
18 | 18 | #include <aws/core/Aws.h> |
| 19 | +#include <google/protobuf/util/message_differencer.h> |
19 | 20 |
|
20 | 21 | using namespace NYdb; |
21 | 22 | using namespace NYdb::NTable; |
22 | 23 |
|
| 24 | +namespace NYdb::NTable { |
| 25 | + |
| 26 | +bool operator==(const TValue& lhs, const TValue& rhs) { |
| 27 | + return google::protobuf::util::MessageDifferencer::Equals(lhs.GetProto(), rhs.GetProto()); |
| 28 | +} |
| 29 | + |
| 30 | +bool operator==(const TKeyBound& lhs, const TKeyBound& rhs) { |
| 31 | + return lhs.GetValue() == rhs.GetValue() && lhs.IsInclusive() == rhs.IsInclusive(); |
| 32 | +} |
| 33 | + |
| 34 | +bool operator==(const TKeyRange& lhs, const TKeyRange& rhs) { |
| 35 | + return lhs.From() == lhs.From() && lhs.To() == rhs.To(); |
| 36 | +} |
| 37 | + |
| 38 | +} |
| 39 | + |
23 | 40 | namespace { |
24 | 41 |
|
25 | 42 | #define DEBUG_HINT (TStringBuilder() << "at line " << __LINE__) |
@@ -194,6 +211,106 @@ void TestIndexTablePartitioningSettingsArePreserved( |
194 | 211 | CheckTableDescription(session, indexTablePath, CreateMinPartitionsChecker(minIndexPartitions, DEBUG_HINT)); |
195 | 212 | } |
196 | 213 |
|
| 214 | +void TestTableSplitBoundariesArePreserved( |
| 215 | + const char* table, ui64 partitions, TSession& session, TBackupFunction&& backup, TRestoreFunction&& restore |
| 216 | +) { |
| 217 | + ExecuteDataDefinitionQuery(session, Sprintf(R"( |
| 218 | + CREATE TABLE `%s` ( |
| 219 | + Key Uint32, |
| 220 | + Value Utf8, |
| 221 | + PRIMARY KEY (Key) |
| 222 | + ) |
| 223 | + WITH ( |
| 224 | + PARTITION_AT_KEYS = (1, 2, 4, 8, 16, 32, 64, 128, 256) |
| 225 | + ); |
| 226 | + )", |
| 227 | + table |
| 228 | + )); |
| 229 | + const auto describeSettings = TDescribeTableSettings() |
| 230 | + .WithTableStatistics(true) |
| 231 | + .WithKeyShardBoundary(true); |
| 232 | + const auto originalTableDescription = GetTableDescription(session, table, describeSettings); |
| 233 | + UNIT_ASSERT_VALUES_EQUAL(originalTableDescription.GetPartitionsCount(), partitions); |
| 234 | + const auto& originalKeyRanges = originalTableDescription.GetKeyRanges(); |
| 235 | + UNIT_ASSERT_VALUES_EQUAL(originalKeyRanges.size(), partitions); |
| 236 | + |
| 237 | + backup(table); |
| 238 | + |
| 239 | + ExecuteDataDefinitionQuery(session, Sprintf(R"( |
| 240 | + DROP TABLE `%s`; |
| 241 | + )", table |
| 242 | + )); |
| 243 | + |
| 244 | + restore(table); |
| 245 | + const auto restoredTableDescription = GetTableDescription(session, table, describeSettings); |
| 246 | + UNIT_ASSERT_VALUES_EQUAL(restoredTableDescription.GetPartitionsCount(), partitions); |
| 247 | + const auto& restoredKeyRanges = restoredTableDescription.GetKeyRanges(); |
| 248 | + UNIT_ASSERT_VALUES_EQUAL(restoredKeyRanges.size(), partitions); |
| 249 | + UNIT_ASSERT_EQUAL(restoredTableDescription.GetKeyRanges(), originalKeyRanges); |
| 250 | +} |
| 251 | + |
| 252 | +void TestIndexTableSplitBoundariesArePreserved( |
| 253 | + const char* table, const char* index, ui64 indexPartitions, TSession& session, |
| 254 | + TBackupFunction&& backup, TRestoreFunction&& restore |
| 255 | +) { |
| 256 | + const TString indexTablePath = JoinFsPaths(table, index, "indexImplTable"); |
| 257 | + |
| 258 | + { |
| 259 | + TExplicitPartitions indexPartitionBoundaries; |
| 260 | + for (ui32 boundary : {1, 2, 4, 8, 16, 32, 64, 128, 256}) { |
| 261 | + indexPartitionBoundaries.AppendSplitPoints( |
| 262 | + // split boundary is technically always a tuple |
| 263 | + TValueBuilder().BeginTuple().AddElement().OptionalUint32(boundary).EndTuple().Build() |
| 264 | + ); |
| 265 | + } |
| 266 | + // By default indexImplTable has auto partitioning by size enabled, |
| 267 | + // so you must set min partition count for partitions to not merge immediately after indexImplTable is built. |
| 268 | + TPartitioningSettingsBuilder partitioningSettingsBuilder; |
| 269 | + partitioningSettingsBuilder |
| 270 | + .SetMinPartitionsCount(indexPartitions) |
| 271 | + .SetMaxPartitionsCount(indexPartitions); |
| 272 | + |
| 273 | + const auto indexSettings = TGlobalIndexSettings{ |
| 274 | + .PartitioningSettings = partitioningSettingsBuilder.Build(), |
| 275 | + .Partitions = std::move(indexPartitionBoundaries) |
| 276 | + }; |
| 277 | + |
| 278 | + auto tableBuilder = TTableBuilder() |
| 279 | + .AddNullableColumn("Key", EPrimitiveType::Uint32) |
| 280 | + .AddNullableColumn("Value", EPrimitiveType::Uint32) |
| 281 | + .SetPrimaryKeyColumn("Key") |
| 282 | + .AddSecondaryIndex("byValue", EIndexType::GlobalSync, { "Value" }, {}, indexSettings); |
| 283 | + |
| 284 | + const auto result = session.CreateTable(table, tableBuilder.Build()).ExtractValueSync(); |
| 285 | + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); |
| 286 | + } |
| 287 | + const auto describeSettings = TDescribeTableSettings() |
| 288 | + .WithTableStatistics(true) |
| 289 | + .WithKeyShardBoundary(true); |
| 290 | + const auto originalIndexTableDescription = GetTableDescription( |
| 291 | + session, indexTablePath, describeSettings |
| 292 | + ); |
| 293 | + UNIT_ASSERT_VALUES_EQUAL(originalIndexTableDescription.GetPartitionsCount(), indexPartitions); |
| 294 | + const auto& originalKeyRanges = originalIndexTableDescription.GetKeyRanges(); |
| 295 | + UNIT_ASSERT_VALUES_EQUAL(originalKeyRanges.size(), indexPartitions); |
| 296 | + |
| 297 | + backup(table); |
| 298 | + |
| 299 | + ExecuteDataDefinitionQuery(session, Sprintf(R"( |
| 300 | + DROP TABLE `%s`; |
| 301 | + )", table |
| 302 | + )); |
| 303 | + |
| 304 | + restore(table); |
| 305 | + const auto restoredIndexTableDescription = GetTableDescription( |
| 306 | + session, indexTablePath, describeSettings |
| 307 | + ); |
| 308 | + UNIT_ASSERT_VALUES_EQUAL(restoredIndexTableDescription.GetPartitionsCount(), indexPartitions); |
| 309 | + const auto& restoredKeyRanges = restoredIndexTableDescription.GetKeyRanges(); |
| 310 | + UNIT_ASSERT_VALUES_EQUAL(restoredKeyRanges.size(), indexPartitions); |
| 311 | + UNIT_ASSERT_EQUAL(restoredKeyRanges, originalKeyRanges); |
| 312 | +} |
| 313 | + |
197 | 314 | } |
198 | 315 |
|
199 | 316 | Y_UNIT_TEST_SUITE(BackupRestore) { |
@@ -279,6 +396,48 @@ Y_UNIT_TEST_SUITE(BackupRestore) { |
279 | 396 | ); |
280 | 397 | } |
281 | 398 |
|
| 399 | + Y_UNIT_TEST(RestoreTableSplitBoundaries) { |
| 400 | + TKikimrWithGrpcAndRootSchema server; |
| 401 | + auto driver = TDriver(TDriverConfig().SetEndpoint(Sprintf("localhost:%u", server.GetPort()))); |
| 402 | + TTableClient tableClient(driver); |
| 403 | + auto session = tableClient.GetSession().ExtractValueSync().GetSession(); |
| 404 | + TTempDir tempDir; |
| 405 | + const auto& pathToBackup = tempDir.Path(); |
| 406 | + |
| 407 | + constexpr const char* table = "/Root/table"; |
| 408 | + constexpr ui64 partitions = 10; |
| 409 | + |
| 410 | + TestTableSplitBoundariesArePreserved( |
| 411 | + table, |
| 412 | + partitions, |
| 413 | + session, |
| 414 | + CreateBackupLambda(driver, pathToBackup, true), |
| 415 | + CreateRestoreLambda(driver, pathToBackup) |
| 416 | + ); |
| 417 | + } |
| 418 | + |
| 419 | + Y_UNIT_TEST(RestoreIndexTableSplitBoundaries) { |
| 420 | + TKikimrWithGrpcAndRootSchema server; |
| 421 | + auto driver = TDriver(TDriverConfig().SetEndpoint(Sprintf("localhost:%u", server.GetPort()))); |
| 422 | + TTableClient tableClient(driver); |
| 423 | + auto session = tableClient.GetSession().ExtractValueSync().GetSession(); |
| 424 | + TTempDir tempDir; |
| 425 | + const auto& pathToBackup = tempDir.Path(); |
| 426 | + |
| 427 | + constexpr const char* table = "/Root/table"; |
| 428 | + constexpr const char* index = "byValue"; |
| 429 | + constexpr ui64 indexPartitions = 10; |
| 430 | + |
| 431 | + TestIndexTableSplitBoundariesArePreserved( |
| 432 | + table, |
| 433 | + index, |
| 434 | + indexPartitions, |
| 435 | + session, |
| 436 | + CreateBackupLambda(driver, pathToBackup, true), |
| 437 | + CreateRestoreLambda(driver, pathToBackup) |
| 438 | + ); |
| 439 | + } |
| 440 | + |
282 | 441 | } |
283 | 442 |
|
284 | 443 | Y_UNIT_TEST_SUITE(BackupRestoreS3) { |
@@ -455,4 +614,34 @@ Y_UNIT_TEST_SUITE(BackupRestoreS3) { |
455 | 614 | ); |
456 | 615 | } |
457 | 616 |
|
| 617 | + Y_UNIT_TEST(RestoreTableSplitBoundaries) { |
| 618 | + TS3TestEnv testEnv; |
| 619 | + constexpr const char* table = "/Root/table"; |
| 620 | + constexpr ui64 partitions = 10; |
| 621 | + |
| 622 | + TestTableSplitBoundariesArePreserved( |
| 623 | + table, |
| 624 | + partitions, |
| 625 | + testEnv.GetSession(), |
| 626 | + CreateBackupLambda(testEnv.GetDriver(), testEnv.GetS3Port()), |
| 627 | + CreateRestoreLambda(testEnv.GetDriver(), testEnv.GetS3Port()) |
| 628 | + ); |
| 629 | + } |
| 630 | + |
| 631 | + Y_UNIT_TEST(RestoreIndexTableSplitBoundaries) { |
| 632 | + TS3TestEnv testEnv; |
| 633 | + constexpr const char* table = "/Root/table"; |
| 634 | + constexpr const char* index = "byValue"; |
| 635 | + constexpr ui64 indexPartitions = 10; |
| 636 | + |
| 637 | + TestIndexTableSplitBoundariesArePreserved( |
| 638 | + table, |
| 639 | + index, |
| 640 | + indexPartitions, |
| 641 | + testEnv.GetSession(), |
| 642 | + CreateBackupLambda(testEnv.GetDriver(), testEnv.GetS3Port()), |
| 643 | + CreateRestoreLambda(testEnv.GetDriver(), testEnv.GetS3Port()) |
| 644 | + ); |
| 645 | + } |
| 646 | + |
458 | 647 | } |
0 commit comments