2323import static org .apache .hadoop .hdds .scm .HddsTestUtils .getECContainer ;
2424import static org .apache .hadoop .hdds .scm .HddsTestUtils .getReplicas ;
2525import static org .junit .jupiter .api .Assertions .assertEquals ;
26+ import static org .junit .jupiter .api .Assertions .fail ;
2627import static org .mockito .Mockito .any ;
2728import static org .mockito .Mockito .doAnswer ;
2829import static org .mockito .Mockito .mock ;
7475import org .junit .jupiter .api .BeforeEach ;
7576import org .junit .jupiter .api .Test ;
7677import org .junit .jupiter .api .io .TempDir ;
78+ import org .junit .jupiter .params .ParameterizedTest ;
79+ import org .junit .jupiter .params .provider .EnumSource ;
7780
7881/**
7982 * Test the behaviour of the ContainerReportHandler.
@@ -166,7 +169,7 @@ private void testReplicaIndexUpdate(ContainerInfo container,
166169 Map <DatanodeDetails , Integer > expectedReplicaMap ) {
167170 final ContainerReportsProto containerReport = getContainerReportsProto (
168171 container .containerID (), ContainerReplicaProto .State .CLOSED ,
169- dn .getUuidString (), 2000000000L , 100000000L , replicaIndex );
172+ dn .getUuidString (), 2000000000L , 100000000L , 10000L , replicaIndex );
170173 final ContainerReportFromDatanode containerReportFromDatanode =
171174 new ContainerReportFromDatanode (dn , containerReport );
172175 final ContainerReportHandler reportHandler = new ContainerReportHandler (
@@ -601,7 +604,7 @@ private void createAndHandleContainerReport(ContainerID containerID,
601604
602605 @ Test
603606 public void testClosingToQuasiClosed ()
604- throws NodeNotFoundException , IOException , TimeoutException {
607+ throws NodeNotFoundException , IOException {
605608 /*
606609 * The container is in CLOSING state and all the replicas are in
607610 * OPEN/CLOSING state.
@@ -668,7 +671,7 @@ public void testClosingToQuasiClosed()
668671
669672 @ Test
670673 public void testQuasiClosedToClosed ()
671- throws NodeNotFoundException , IOException , TimeoutException {
674+ throws NodeNotFoundException , IOException {
672675 /*
673676 * The container is in QUASI_CLOSED state.
674677 * - One of the replica is in QUASI_CLOSED state
@@ -737,6 +740,52 @@ public void testQuasiClosedToClosed()
737740 assertEquals (LifeCycleState .CLOSED , containerManager .getContainer (containerOne .containerID ()).getState ());
738741 }
739742
743+ @ ParameterizedTest
744+ @ EnumSource (value = LifeCycleState .class , names = {"CLOSING" , "QUASI_CLOSED" })
745+ public void testContainerStateTransitionToClosedWithMismatchingBCSID (LifeCycleState lcState )
746+ throws NodeNotFoundException , IOException {
747+ /*
748+ * Negative test. When a replica with a (lower) mismatching bcsId gets reported,
749+ * expect the ContainerReportHandler thread to not throw uncaught exception.
750+ * (That exception lead to ContainerReportHandler thread crash before HDDS-12150.)
751+ */
752+ final ContainerReportHandler reportHandler =
753+ new ContainerReportHandler (nodeManager , containerManager );
754+ final Iterator <DatanodeDetails > nodeIterator =
755+ nodeManager .getNodes (NodeStatus .inServiceHealthy ()).iterator ();
756+
757+ final DatanodeDetails dn1 = nodeIterator .next ();
758+ final DatanodeDetails dn2 = nodeIterator .next ();
759+ final DatanodeDetails dn3 = nodeIterator .next ();
760+
761+ // Initial sequenceId 10000L is set here
762+ final ContainerInfo container1 = getContainer (lcState );
763+
764+ nodeManager .addContainer (dn1 , container1 .containerID ());
765+ nodeManager .addContainer (dn2 , container1 .containerID ());
766+ nodeManager .addContainer (dn3 , container1 .containerID ());
767+
768+ containerStateManager .addContainer (container1 .getProtobuf ());
769+
770+ // Generate container report with replica in CLOSED state with intentional lower bcsId
771+ final ContainerReportsProto containerReport = getContainerReportsProto (
772+ container1 .containerID (), ContainerReplicaProto .State .CLOSED ,
773+ dn1 .getUuidString (),
774+ 2000L );
775+ final ContainerReportFromDatanode containerReportFromDatanode =
776+ new ContainerReportFromDatanode (dn1 , containerReport );
777+
778+ // Handler should NOT throw IllegalArgumentException
779+ try {
780+ reportHandler .onMessage (containerReportFromDatanode , publisher );
781+ } catch (IllegalArgumentException iaEx ) {
782+ fail ("Handler should not throw IllegalArgumentException: " + iaEx .getMessage ());
783+ }
784+
785+ // Because the container report is ignored, the container remains in the same previous state in SCM
786+ assertEquals (lcState , containerManager .getContainer (container1 .containerID ()).getState ());
787+ }
788+
740789 @ Test
741790 public void openContainerKeyAndBytesUsedUpdatedToMinimumOfAllReplicas ()
742791 throws IOException , TimeoutException {
@@ -1092,7 +1141,7 @@ private ContainerReportFromDatanode getContainerReportFromDatanode(
10921141 DatanodeDetails dn , long bytesUsed , long keyCount , int replicaIndex ) {
10931142 ContainerReportsProto containerReport = getContainerReportsProto (
10941143 containerId , state , dn .getUuidString (), bytesUsed , keyCount ,
1095- replicaIndex );
1144+ 10000L , replicaIndex );
10961145
10971146 return new ContainerReportFromDatanode (dn , containerReport );
10981147 }
@@ -1101,20 +1150,34 @@ protected static ContainerReportsProto getContainerReportsProto(
11011150 final ContainerID containerId , final ContainerReplicaProto .State state ,
11021151 final String originNodeId ) {
11031152 return getContainerReportsProto (containerId , state , originNodeId ,
1104- 2000000000L , 100000000L , 0 );
1153+ 2000000000L , 100000000L , 10000L , 0 );
1154+ }
1155+
1156+ protected static ContainerReportsProto getContainerReportsProto (
1157+ final ContainerID containerId , final ContainerReplicaProto .State state ,
1158+ final String originNodeId , final long bcsId ) {
1159+ return getContainerReportsProto (containerId , state , originNodeId ,
1160+ 2000000000L , 100000000L , bcsId , 0 );
11051161 }
11061162
11071163 protected static ContainerReportsProto getContainerReportsProto (
11081164 final ContainerID containerId , final ContainerReplicaProto .State state ,
11091165 final String originNodeId , int replicaIndex ) {
11101166 return getContainerReportsProto (containerId , state , originNodeId ,
1111- 2000000000L , 100000000L , replicaIndex );
1167+ 2000000000L , 100000000L , 10000L , replicaIndex );
1168+ }
1169+
1170+ protected static ContainerReportsProto getContainerReportsProto (
1171+ final ContainerID containerId , final ContainerReplicaProto .State state ,
1172+ final String originNodeId , final long bcsId , int replicaIndex ) {
1173+ return getContainerReportsProto (containerId , state , originNodeId ,
1174+ 2000000000L , 100000000L , bcsId , replicaIndex );
11121175 }
11131176
11141177 protected static ContainerReportsProto getContainerReportsProto (
11151178 final ContainerID containerId , final ContainerReplicaProto .State state ,
11161179 final String originNodeId , final long usedBytes , final long keyCount ,
1117- final int replicaIndex ) {
1180+ final long bcsId , final int replicaIndex ) {
11181181 final ContainerReportsProto .Builder crBuilder =
11191182 ContainerReportsProto .newBuilder ();
11201183 final ContainerReplicaProto replicaProto =
@@ -1130,7 +1193,7 @@ protected static ContainerReportsProto getContainerReportsProto(
11301193 .setWriteCount (100000000L )
11311194 .setReadBytes (2000000000L )
11321195 .setWriteBytes (2000000000L )
1133- .setBlockCommitSequenceId (10000L )
1196+ .setBlockCommitSequenceId (bcsId )
11341197 .setDeleteTransactionId (0 )
11351198 .setReplicaIndex (replicaIndex )
11361199 .build ();
0 commit comments