HDDS-1413. Attempt to fix TestCloseContainerCommandHandler by adjusting timeouts

Signed-off-by: Anu Engineer <aengineer@apache.org>
This commit is contained in:
Doroszlai, Attila 2019-08-29 18:01:21 +02:00 committed by Anu Engineer
parent c4411f7fdf
commit a2d083f2c5
1 changed files with 133 additions and 257 deletions

View File

@ -16,306 +16,187 @@
*/ */
package org.apache.hadoop.ozone.container.common.statemachine.commandhandler; package org.apache.hadoop.ozone.container.common.statemachine.commandhandler;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdds.HddsConfigKeys;
import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.DatanodeDetails; import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos; import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import org.apache.hadoop.hdds.scm.container.ContainerID;
import org.apache.hadoop.hdds.scm.pipeline.PipelineID; import org.apache.hadoop.hdds.scm.pipeline.PipelineID;
import org.apache.hadoop.ozone.OzoneConfigKeys; import org.apache.hadoop.ozone.container.common.impl.ContainerSet;
import org.apache.hadoop.ozone.container.common.interfaces.Container; import org.apache.hadoop.ozone.container.common.interfaces.Container;
import org.apache.hadoop.ozone.container.common.interfaces.Handler;
import org.apache.hadoop.ozone.container.common.statemachine import org.apache.hadoop.ozone.container.common.statemachine
.DatanodeStateMachine; .DatanodeStateMachine;
import org.apache.hadoop.ozone.container.common.statemachine.StateContext; import org.apache.hadoop.ozone.container.common.statemachine.StateContext;
import org.apache.hadoop.ozone.container.common.transport.server.XceiverServerSpi;
import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainer;
import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainerData;
import org.apache.hadoop.ozone.container.ozoneimpl.ContainerController;
import org.apache.hadoop.ozone.container.ozoneimpl.OzoneContainer; import org.apache.hadoop.ozone.container.ozoneimpl.OzoneContainer;
import org.apache.hadoop.ozone.protocol.commands.CloseContainerCommand; import org.apache.hadoop.ozone.protocol.commands.CloseContainerCommand;
import org.apache.hadoop.test.GenericTestUtils; import org.junit.Before;
import org.apache.hadoop.hdds.ratis.RatisHelper;
import org.apache.ratis.client.RaftClient;
import org.apache.ratis.protocol.RaftGroup;
import org.apache.ratis.protocol.RaftGroupId;
import org.apache.ratis.protocol.RaftPeer;
import org.apache.ratis.retry.RetryPolicy;
import org.apache.ratis.rpc.SupportedRpcType;
import org.apache.ratis.util.TimeDuration;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.Random;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static java.util.Collections.singletonMap;
import static org.apache.hadoop.ozone.OzoneConsts.GB;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/** /**
* Test cases to verify CloseContainerCommandHandler in datanode. * Test cases to verify CloseContainerCommandHandler in datanode.
*/ */
public class TestCloseContainerCommandHandler { public class TestCloseContainerCommandHandler {
private final StateContext context = Mockito.mock(StateContext.class); private static final long CONTAINER_ID = 123L;
private final Random random = new Random();
private static File testDir;
@Test private OzoneContainer ozoneContainer;
public void testCloseContainerViaRatis() private StateContext context;
throws Exception { private XceiverServerSpi writeChannel;
final OzoneConfiguration conf = new OzoneConfiguration(); private Container container;
final DatanodeDetails datanodeDetails = randomDatanodeDetails(); private Handler containerHandler;
final OzoneContainer ozoneContainer = private PipelineID pipelineID;
getOzoneContainer(conf, datanodeDetails); private PipelineID nonExistentPipelineID = PipelineID.randomId();
ozoneContainer.start(UUID.randomUUID().toString());
try {
final Container container =
createContainer(conf, datanodeDetails, ozoneContainer);
Mockito.verify(context.getParent(),
Mockito.times(1)).triggerHeartbeat();
final long containerId = container.getContainerData().getContainerID();
final PipelineID pipelineId = PipelineID.valueOf(UUID.fromString(
container.getContainerData().getOriginPipelineId()));
// We have created a container via ratis. private CloseContainerCommandHandler subject =
// Now close the container on ratis.
final CloseContainerCommandHandler closeHandler =
new CloseContainerCommandHandler(); new CloseContainerCommandHandler();
final CloseContainerCommand command = new CloseContainerCommand(
containerId, pipelineId);
closeHandler.handle(command, ozoneContainer, context, null); @Before
public void before() throws Exception {
context = mock(StateContext.class);
DatanodeStateMachine dnStateMachine = mock(DatanodeStateMachine.class);
when(dnStateMachine.getDatanodeDetails())
.thenReturn(randomDatanodeDetails());
when(context.getParent()).thenReturn(dnStateMachine);
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.CLOSED, pipelineID = PipelineID.randomId();
ozoneContainer.getContainerSet().getContainer(containerId)
.getContainerState());
Mockito.verify(context.getParent(), KeyValueContainerData data = new KeyValueContainerData(CONTAINER_ID, GB,
Mockito.times(3)).triggerHeartbeat(); pipelineID.getId().toString(), null);
} finally {
ozoneContainer.stop(); container = new KeyValueContainer(data, new OzoneConfiguration());
} ContainerSet containerSet = new ContainerSet();
containerSet.addContainer(container);
containerHandler = mock(Handler.class);
ContainerController controller = new ContainerController(containerSet,
singletonMap(ContainerProtos.ContainerType.KeyValueContainer,
containerHandler));
writeChannel = mock(XceiverServerSpi.class);
ozoneContainer = mock(OzoneContainer.class);
when(ozoneContainer.getController()).thenReturn(controller);
when(ozoneContainer.getContainerSet()).thenReturn(containerSet);
when(ozoneContainer.getWriteChannel()).thenReturn(writeChannel);
when(writeChannel.isExist(pipelineID.getProtobuf())).thenReturn(true);
when(writeChannel.isExist(nonExistentPipelineID.getProtobuf()))
.thenReturn(false);
} }
@Test @Test
public void testCloseContainerViaStandalone() public void closeContainerWithPipeline() throws Exception {
throws Exception { // close a container that's associated with an existing pipeline
final OzoneConfiguration conf = new OzoneConfiguration(); subject.handle(closeWithKnownPipeline(), ozoneContainer, context, null);
final DatanodeDetails datanodeDetails = randomDatanodeDetails();
final OzoneContainer ozoneContainer =
getOzoneContainer(conf, datanodeDetails);
ozoneContainer.start(UUID.randomUUID().toString());
try {
final Container container =
createContainer(conf, datanodeDetails, ozoneContainer);
Mockito.verify(context.getParent(),
Mockito.times(1)).triggerHeartbeat();
final long containerId = container.getContainerData().getContainerID();
// To quasi close specify a pipeline which doesn't exist in the datanode.
final PipelineID pipelineId = PipelineID.randomId();
// We have created a container via ratis. Now quasi close it. verify(containerHandler)
final CloseContainerCommandHandler closeHandler = .markContainerForClose(container);
new CloseContainerCommandHandler(); verify(writeChannel)
final CloseContainerCommand command = new CloseContainerCommand( .submitRequest(any(), eq(pipelineID.getProtobuf()));
containerId, pipelineId); verify(containerHandler, never())
.quasiCloseContainer(container);
closeHandler.handle(command, ozoneContainer, context, null);
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.QUASI_CLOSED,
ozoneContainer.getContainerSet().getContainer(containerId)
.getContainerState());
Mockito.verify(context.getParent(),
Mockito.times(3)).triggerHeartbeat();
} finally {
ozoneContainer.stop();
}
} }
@Test @Test
public void testQuasiCloseToClose() throws Exception { public void closeContainerWithoutPipeline() throws IOException {
final OzoneConfiguration conf = new OzoneConfiguration(); // close a container that's NOT associated with an open pipeline
final DatanodeDetails datanodeDetails = randomDatanodeDetails(); subject.handle(closeWithUnknownPipeline(), ozoneContainer, context, null);
final OzoneContainer ozoneContainer =
getOzoneContainer(conf, datanodeDetails);
ozoneContainer.start(UUID.randomUUID().toString());
try {
final Container container =
createContainer(conf, datanodeDetails, ozoneContainer);
Mockito.verify(context.getParent(),
Mockito.times(1)).triggerHeartbeat();
final long containerId = container.getContainerData().getContainerID();
// A pipeline which doesn't exist in the datanode.
final PipelineID pipelineId = PipelineID.randomId();
// We have created a container via ratis. Now quasi close it. verify(containerHandler)
final CloseContainerCommandHandler closeHandler = .markContainerForClose(container);
new CloseContainerCommandHandler(); verify(writeChannel, never())
final CloseContainerCommand command = new CloseContainerCommand( .submitRequest(any(), any());
containerId, pipelineId); verify(containerHandler)
.quasiCloseContainer(container);
closeHandler.handle(command, ozoneContainer, context, null);
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.QUASI_CLOSED,
ozoneContainer.getContainerSet().getContainer(containerId)
.getContainerState());
Mockito.verify(context.getParent(),
Mockito.times(3)).triggerHeartbeat();
// The container is quasi closed. Force close the container now.
final CloseContainerCommand closeCommand = new CloseContainerCommand(
containerId, pipelineId, true);
closeHandler.handle(closeCommand, ozoneContainer, context, null);
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.CLOSED,
ozoneContainer.getContainerSet().getContainer(containerId)
.getContainerState());
Mockito.verify(context.getParent(),
Mockito.times(4)).triggerHeartbeat();
} finally {
ozoneContainer.stop();
}
} }
@Test @Test
public void testForceCloseOpenContainer() throws Exception { public void forceCloseQuasiClosedContainer() throws Exception {
final OzoneConfiguration conf = new OzoneConfiguration(); // force-close a container that's already quasi closed
final DatanodeDetails datanodeDetails = randomDatanodeDetails(); container.getContainerData()
final OzoneContainer ozoneContainer = .setState(ContainerProtos.ContainerDataProto.State.QUASI_CLOSED);
getOzoneContainer(conf, datanodeDetails);
ozoneContainer.start(UUID.randomUUID().toString());
try {
final Container container =
createContainer(conf, datanodeDetails, ozoneContainer);
Mockito.verify(context.getParent(),
Mockito.times(1)).triggerHeartbeat();
final long containerId = container.getContainerData().getContainerID();
// A pipeline which doesn't exist in the datanode.
final PipelineID pipelineId = PipelineID.randomId();
final CloseContainerCommandHandler closeHandler = subject.handle(forceCloseWithoutPipeline(), ozoneContainer, context, null);
new CloseContainerCommandHandler();
final CloseContainerCommand closeCommand = new CloseContainerCommand( verify(writeChannel, never())
containerId, pipelineId, true); .submitRequest(any(), any());
verify(containerHandler)
closeHandler.handle(closeCommand, ozoneContainer, context, null); .closeContainer(container);
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.CLOSED,
ozoneContainer.getContainerSet().getContainer(containerId)
.getContainerState());
Mockito.verify(context.getParent(),
Mockito.times(3)).triggerHeartbeat();
} finally {
ozoneContainer.stop();
}
} }
@Test @Test
public void testQuasiCloseClosedContainer() public void forceCloseOpenContainer() throws Exception {
throws Exception { // force-close a container that's NOT associated with an open pipeline
final OzoneConfiguration conf = new OzoneConfiguration(); subject.handle(forceCloseWithoutPipeline(), ozoneContainer, context, null);
final DatanodeDetails datanodeDetails = randomDatanodeDetails();
final OzoneContainer ozoneContainer = getOzoneContainer(
conf, datanodeDetails);
ozoneContainer.start(UUID.randomUUID().toString());
try {
final Container container = createContainer(
conf, datanodeDetails, ozoneContainer);
Mockito.verify(context.getParent(),
Mockito.times(1)).triggerHeartbeat();
final long containerId = container.getContainerData().getContainerID();
final PipelineID pipelineId = PipelineID.valueOf(UUID.fromString(
container.getContainerData().getOriginPipelineId()));
final CloseContainerCommandHandler closeHandler = verify(writeChannel, never())
new CloseContainerCommandHandler(); .submitRequest(any(), any());
final CloseContainerCommand closeCommand = new CloseContainerCommand( verify(containerHandler)
containerId, pipelineId); .closeContainer(container);
closeHandler.handle(closeCommand, ozoneContainer, context, null);
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.CLOSED,
ozoneContainer.getContainerSet().getContainer(containerId)
.getContainerState());
// The container is closed, now we send close command with
// pipeline id which doesn't exist.
// This should cause the datanode to trigger quasi close, since the
// container is already closed, this should do nothing.
// The command should not fail either.
final PipelineID randomPipeline = PipelineID.randomId();
final CloseContainerCommand quasiCloseCommand =
new CloseContainerCommand(containerId, randomPipeline);
closeHandler.handle(quasiCloseCommand, ozoneContainer, context, null);
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.CLOSED,
ozoneContainer.getContainerSet().getContainer(containerId)
.getContainerState());
} finally {
ozoneContainer.stop();
}
} }
private OzoneContainer getOzoneContainer(final OzoneConfiguration conf, @Test
final DatanodeDetails datanodeDetails) throws IOException { public void forceCloseOpenContainerWithPipeline() throws Exception {
testDir = GenericTestUtils.getTestDir( // force-close a container that's associated with an existing pipeline
TestCloseContainerCommandHandler.class.getName() + UUID.randomUUID()); subject.handle(forceCloseWithPipeline(), ozoneContainer, context, null);
conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, testDir.getPath());
conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_KEY, testDir.getPath());
conf.setBoolean(OzoneConfigKeys.DFS_CONTAINER_RATIS_IPC_RANDOM_PORT, true);
conf.setBoolean(OzoneConfigKeys.DFS_CONTAINER_IPC_RANDOM_PORT, true);
final DatanodeStateMachine datanodeStateMachine = Mockito.mock( verify(containerHandler)
DatanodeStateMachine.class); .markContainerForClose(container);
Mockito.when(datanodeStateMachine.getDatanodeDetails()) verify(writeChannel, never())
.thenReturn(datanodeDetails); .submitRequest(any(), any());
Mockito.when(context.getParent()).thenReturn(datanodeStateMachine); verify(containerHandler, never())
final OzoneContainer ozoneContainer = new OzoneContainer( .quasiCloseContainer(container);
datanodeDetails, conf, context, null); verify(containerHandler, never())
return ozoneContainer; .closeContainer(container);
} }
private Container createContainer(final Configuration conf, @Test
final DatanodeDetails datanodeDetails, public void closeAlreadyClosedContainer() throws Exception {
final OzoneContainer ozoneContainer) throws Exception { container.getContainerData()
final PipelineID pipelineID = PipelineID.randomId(); .setState(ContainerProtos.ContainerDataProto.State.CLOSED);
final RaftGroupId raftGroupId = RaftGroupId.valueOf(pipelineID.getId());
final RetryPolicy retryPolicy = RatisHelper.createRetryPolicy(conf);
final RaftPeer peer = RatisHelper.toRaftPeer(datanodeDetails);
final RaftGroup group = RatisHelper.newRaftGroup(raftGroupId,
Collections.singleton(datanodeDetails));
final int maxOutstandingRequests = 100;
final RaftClient client = RatisHelper
.newRaftClient(SupportedRpcType.GRPC, peer, retryPolicy,
maxOutstandingRequests,
TimeDuration.valueOf(3, TimeUnit.SECONDS));
Assert.assertTrue(client.groupAdd(group, peer.getId()).isSuccess());
Thread.sleep(10000);
final ContainerID containerId = ContainerID.valueof(
random.nextLong() & Long.MAX_VALUE);
ContainerProtos.ContainerCommandRequestProto.Builder request =
ContainerProtos.ContainerCommandRequestProto.newBuilder();
request.setCmdType(ContainerProtos.Type.CreateContainer);
request.setContainerID(containerId.getId());
request.setCreateContainer(
ContainerProtos.CreateContainerRequestProto.getDefaultInstance());
request.setDatanodeUuid(datanodeDetails.getUuidString());
ozoneContainer.getWriteChannel().submitRequest(
request.build(), pipelineID.getProtobuf());
final Container container = ozoneContainer.getContainerSet().getContainer( // Since the container is already closed, these commands should do nothing,
containerId.getId()); // neither should they fail
Assert.assertEquals(ContainerProtos.ContainerDataProto.State.OPEN, subject.handle(closeWithUnknownPipeline(), ozoneContainer, context, null);
container.getContainerState()); subject.handle(closeWithKnownPipeline(), ozoneContainer, context, null);
return container;
verify(containerHandler, never())
.markContainerForClose(container);
verify(containerHandler, never())
.quasiCloseContainer(container);
verify(containerHandler, never())
.closeContainer(container);
verify(writeChannel, never())
.submitRequest(any(), any());
}
private CloseContainerCommand closeWithKnownPipeline() {
return new CloseContainerCommand(CONTAINER_ID, pipelineID);
}
private CloseContainerCommand closeWithUnknownPipeline() {
return new CloseContainerCommand(CONTAINER_ID, nonExistentPipelineID);
}
private CloseContainerCommand forceCloseWithPipeline() {
return new CloseContainerCommand(CONTAINER_ID, pipelineID, true);
}
private CloseContainerCommand forceCloseWithoutPipeline() {
return new CloseContainerCommand(CONTAINER_ID, nonExistentPipelineID, true);
} }
/** /**
@ -339,9 +220,4 @@ public class TestCloseContainerCommandHandler {
.addPort(restPort); .addPort(restPort);
return builder.build(); return builder.build();
} }
@AfterClass
public static void teardown() throws IOException {
FileUtils.deleteDirectory(testDir);
}
} }