@@ -714,6 +714,133 @@ Y_UNIT_TEST_SUITE(BsControllerConfig) {
714
714
});
715
715
}
716
716
717
+ Y_UNIT_TEST (MergeIntersectingBoxes) {
718
+ const ui32 numNodes = 50 ;
719
+ const ui32 numNodes1 = 20 ;
720
+ const ui32 numGroups1 = numNodes * 3 ;
721
+ const ui32 numGroups2 = numNodes1 * 4 ;
722
+ TEnvironmentSetup env (numNodes, 1 );
723
+ RunTestWithReboots (env.TabletIds , [&] { return env.PrepareInitialEventsFilter (); }, [&](const TString& dispatchName, std::function<void (TTestActorRuntime&)> setup, bool & outActiveZone) {
724
+ TFinalizer finalizer (env);
725
+ env.Prepare (dispatchName, setup, outActiveZone);
726
+
727
+ TVector<TEnvironmentSetup::TNodeRecord> nodes1, nodes2;
728
+ for (const auto & node : env.GetNodes ()) {
729
+ nodes1.push_back (node);
730
+ if (nodes2.size () < numNodes1) {
731
+ nodes2.push_back (node);
732
+ }
733
+ }
734
+
735
+ TSet<ui32> nodeIds1, nodeIds2;
736
+ for (const auto & item : nodes1) {
737
+ nodeIds1.insert (std::get<2 >(item));
738
+ }
739
+ for (const auto & item : nodes2) {
740
+ nodeIds2.insert (std::get<2 >(item));
741
+ }
742
+
743
+ NKikimrBlobStorage::TConfigRequest request;
744
+ env.DefineBox (1 , " first box" , {
745
+ {" /dev/disk1" , NKikimrBlobStorage::ROT, false , false , 0 },
746
+ {" /dev/disk2" , NKikimrBlobStorage::ROT, true , false , 0 },
747
+ {" /dev/disk3" , NKikimrBlobStorage::SSD, false , false , 0 },
748
+ }, nodes1, request);
749
+ env.DefineStoragePool (1 , 1 , " first storage pool" , numGroups1, NKikimrBlobStorage::ROT, {}, request);
750
+
751
+ NKikimrBlobStorage::TConfigResponse response = env.Invoke (request);
752
+ UNIT_ASSERT (response.GetSuccess ());
753
+
754
+ request.Clear ();
755
+ env.DefineBox (2 , " second box" , {
756
+ {" /dev/disk4" , NKikimrBlobStorage::ROT, false , false , 0 },
757
+ {" /dev/disk5" , NKikimrBlobStorage::ROT, true , false , 0 },
758
+ {" /dev/disk6" , NKikimrBlobStorage::SSD, false , false , 0 },
759
+ }, nodes2, request);
760
+ env.DefineStoragePool (2 , 1 , " second storage pool" , numGroups2, NKikimrBlobStorage::ROT, {}, request);
761
+
762
+ response = env.Invoke (request);
763
+ UNIT_ASSERT_C (response.GetSuccess (), response.GetErrorDescription ());
764
+
765
+ // //////////////////////////////////////////////////////////////////////////////////////////////////////
766
+ // merge boxes
767
+
768
+ request.Clear ();
769
+ auto & cmd = *request.AddCommand ()->MutableMergeBoxes ();
770
+ cmd.SetOriginBoxId (2 );
771
+ cmd.SetOriginBoxGeneration (1 );
772
+ cmd.SetTargetBoxId (1 );
773
+ cmd.SetTargetBoxGeneration (1 );
774
+ auto & item = *cmd.AddStoragePoolIdMap ();
775
+ item.SetOriginStoragePoolId (1 );
776
+ item.SetTargetStoragePoolId (2 );
777
+
778
+ response = env.Invoke (request);
779
+ UNIT_ASSERT (response.GetSuccess ());
780
+
781
+ // //////////////////////////////////////////////////////////////////////////////////////////////////////
782
+ // validate result
783
+
784
+ request.Clear ();
785
+ request.AddCommand ()->MutableReadBox ();
786
+ request.AddCommand ()->MutableReadHostConfig ();
787
+ request.AddCommand ()->MutableQueryBaseConfig ();
788
+ response = env.Invoke (request);
789
+ UNIT_ASSERT (response.GetSuccess ());
790
+
791
+ THashMap<std::tuple<TString, i32>, ui32> nodeMap;
792
+ for (const auto & node : env.GetNodes ()) {
793
+ nodeMap.emplace (std::make_tuple (std::get<0 >(node), std::get<1 >(node)), std::get<2 >(node));
794
+ }
795
+
796
+ // check box nodes
797
+ using TDrives = std::set<TString>;
798
+ THashMap<ui64, TDrives> hostConfigs;
799
+ for (const auto & hostConfig : response.GetStatus (1 ).GetHostConfig ()) {
800
+ std::set<TString>& paths = hostConfigs[hostConfig.GetHostConfigId ()];
801
+ for (const auto & drive : hostConfig.GetDrive ()) {
802
+ const auto [it, inserted] = paths.insert (drive.GetPath ());
803
+ UNIT_ASSERT (inserted);
804
+ }
805
+ }
806
+ const auto & boxes = response.GetStatus (0 ).GetBox ();
807
+ UNIT_ASSERT_VALUES_EQUAL (boxes.size (), 1 );
808
+ const auto & box = boxes.at (0 );
809
+ UNIT_ASSERT_VALUES_EQUAL (box.HostSize (), numNodes);
810
+ for (const auto & item : box.GetHost ()) {
811
+ UNIT_ASSERT (hostConfigs.contains (item.GetHostConfigId ()));
812
+ const auto & hostConfig = hostConfigs[item.GetHostConfigId ()];
813
+ const auto & key = item.GetKey ();
814
+ const auto keyTuple = std::make_tuple (key.GetFqdn (), key.GetIcPort ());
815
+ UNIT_ASSERT (nodeMap.contains (keyTuple));
816
+ const ui32 nodeId = nodeMap[keyTuple];
817
+ if (nodeIds2.contains (nodeId)) { // 6 drives
818
+ UNIT_ASSERT_EQUAL (hostConfig, (TDrives{" /dev/disk1" , " /dev/disk2" , " /dev/disk3" , " /dev/disk4" , " /dev/disk5" , " /dev/disk6" }));
819
+ } else { // 3 drives
820
+ UNIT_ASSERT_EQUAL (hostConfig, (TDrives{" /dev/disk1" , " /dev/disk2" , " /dev/disk3" }));
821
+ }
822
+ }
823
+
824
+ // validate base config
825
+ ui32 num1 = 0 , num2 = 0 ;
826
+ {
827
+ const auto & baseConfig = response.GetStatus (2 ).GetBaseConfig ();
828
+ for (const auto & pdisk : baseConfig.GetPDisk ()) {
829
+ UNIT_ASSERT_VALUES_EQUAL (pdisk.GetBoxId (), 1 );
830
+ }
831
+ for (const auto & group : baseConfig.GetGroup ()) {
832
+ UNIT_ASSERT_VALUES_EQUAL (group.GetBoxId (), 1 );
833
+ const ui64 storagePoolId = group.GetStoragePoolId ();
834
+ UNIT_ASSERT (storagePoolId == 1 || storagePoolId == 2 );
835
+ ++(storagePoolId == 1 ? num1 : num2);
836
+ }
837
+ UNIT_ASSERT_VALUES_EQUAL (num1, numGroups1);
838
+ UNIT_ASSERT_VALUES_EQUAL (num2, numGroups2);
839
+ }
840
+
841
+ });
842
+ }
843
+
717
844
Y_UNIT_TEST (MoveGroups) {
718
845
const ui32 numNodes = 50 ;
719
846
const ui32 numGroups1 = 100 ;
0 commit comments