-
Notifications
You must be signed in to change notification settings - Fork 620
HDDS-11136. Some containers affected by HDDS-8129 may still be in the DELETING state incorrectly #6967
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HDDS-11136. Some containers affected by HDDS-8129 may still be in the DELETING state incorrectly #6967
Changes from 2 commits
5ba6fd6
cc03549
6c0c587
c189cce
869a9e6
6b5dacb
85fb626
b8c9512
55a429d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -131,6 +131,16 @@ void updateContainerState(ContainerID containerID, | |
| LifeCycleEvent event) | ||
| throws IOException, InvalidStateTransitionException; | ||
|
|
||
| /** | ||
| * Bypasses the container state machine to change a container's state from DELETING to CLOSED. This API was | ||
| * specially introduced to fix a bug (HDDS-11136), and should NOT be used in any other context. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can make this description a little more general? We can reference the initial bug, but it's also a decent defensive mechanism against accidental deletion if we end up in this situation another way. |
||
| * | ||
| * @see <a href="https://issues.apache.org/jira/browse/HDDS-11136">HDDS-11136</a> | ||
| * @param containerID id of the container to transition | ||
| * @throws IOException | ||
| */ | ||
| void transitionDeletingToClosedState(ContainerID containerID) throws IOException; | ||
|
|
||
| /** | ||
| * Returns the latest list of replicas for given containerId. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,6 +39,7 @@ | |||||||
| import org.apache.hadoop.hdds.protocol.proto.HddsProtos.LifeCycleState; | ||||||||
| import org.apache.hadoop.hdds.protocol.proto.SCMRatisProtocol.RequestType; | ||||||||
| import org.apache.hadoop.hdds.scm.ScmConfigKeys; | ||||||||
| import org.apache.hadoop.hdds.scm.container.common.helpers.InvalidContainerStateException; | ||||||||
| import org.apache.hadoop.hdds.scm.container.replication.ContainerReplicaPendingOps; | ||||||||
| import org.apache.hadoop.hdds.scm.container.states.ContainerState; | ||||||||
| import org.apache.hadoop.hdds.scm.container.states.ContainerStateMap; | ||||||||
|
|
@@ -367,6 +368,28 @@ public void updateContainerState(final HddsProtos.ContainerID containerID, | |||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public void transitionDeletingToClosedState(HddsProtos.ContainerID containerID) throws IOException { | ||||||||
| final ContainerID id = ContainerID.getFromProtobuf(containerID); | ||||||||
|
|
||||||||
| try (AutoCloseableLock ignored = writeLock(id)) { | ||||||||
| if (containers.contains(id)) { | ||||||||
| final ContainerInfo oldInfo = containers.getContainerInfo(id); | ||||||||
| final LifeCycleState oldState = oldInfo.getState(); | ||||||||
| if (oldState != DELETING) { | ||||||||
| throw new InvalidContainerStateException("Container " + oldInfo + "must be in DELETING state to " + | ||||||||
| "transition to CLOSED."); | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the
Suggested change
|
||||||||
| } | ||||||||
| ExecutionUtil.create(() -> { | ||||||||
| containers.updateState(id, oldState, CLOSED); | ||||||||
| transactionBuffer.addToBuffer(containerStore, id, containers.getContainerInfo(id)); | ||||||||
| }).onException(() -> { | ||||||||
| transactionBuffer.addToBuffer(containerStore, id, oldInfo); | ||||||||
| containers.updateState(id, CLOSED, oldState); | ||||||||
| }).execute(); | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| @Override | ||||||||
| public Set<ContainerReplica> getContainerReplicas(final ContainerID id) { | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,12 +41,14 @@ | |
| import org.apache.hadoop.hdds.scm.pipeline.PipelineManager; | ||
| import org.apache.hadoop.hdds.utils.db.DBStore; | ||
| import org.apache.hadoop.hdds.utils.db.DBStoreBuilder; | ||
| import org.apache.hadoop.ozone.common.statemachine.InvalidStateTransitionException; | ||
| import org.apache.hadoop.ozone.container.common.SCMTestUtils; | ||
| import org.junit.jupiter.api.AfterEach; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.junit.jupiter.api.io.TempDir; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.times; | ||
| import static org.mockito.Mockito.verify; | ||
|
|
@@ -132,6 +134,62 @@ void testUpdateContainerState() throws Exception { | |
| assertEquals(LifeCycleState.CLOSED, containerManager.getContainer(cid).getState()); | ||
| } | ||
|
|
||
| @Test | ||
| void testTransitionDeletingToClosedState() throws IOException, InvalidStateTransitionException { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit. Can we parameterize this by container type instead of duplicating each line for each container? |
||
| // allocate OPEN Ratis and Ec containers, and do a series of state changes to transition them to DELETING | ||
| final ContainerInfo container = containerManager.allocateContainer( | ||
| RatisReplicationConfig.getInstance( | ||
| ReplicationFactor.THREE), "admin"); | ||
| ContainerInfo ecContainer = containerManager.allocateContainer(new ECReplicationConfig(3, 2), "admin"); | ||
| final ContainerID cid = container.containerID(); | ||
| final ContainerID ecCid = ecContainer.containerID(); | ||
| assertEquals(LifeCycleState.OPEN, containerManager.getContainer(cid).getState()); | ||
| assertEquals(LifeCycleState.OPEN, containerManager.getContainer(ecCid).getState()); | ||
|
|
||
| // OPEN -> CLOSING | ||
| containerManager.updateContainerState(cid, | ||
| HddsProtos.LifeCycleEvent.FINALIZE); | ||
| containerManager.updateContainerState(ecCid, HddsProtos.LifeCycleEvent.FINALIZE); | ||
| assertEquals(LifeCycleState.CLOSING, containerManager.getContainer(cid).getState()); | ||
| assertEquals(LifeCycleState.CLOSING, containerManager.getContainer(ecCid).getState()); | ||
|
|
||
| // CLOSING -> CLOSED | ||
| containerManager.updateContainerState(cid, HddsProtos.LifeCycleEvent.CLOSE); | ||
| containerManager.updateContainerState(ecCid, HddsProtos.LifeCycleEvent.CLOSE); | ||
| assertEquals(LifeCycleState.CLOSED, containerManager.getContainer(cid).getState()); | ||
| assertEquals(LifeCycleState.CLOSED, containerManager.getContainer(ecCid).getState()); | ||
|
|
||
| // CLOSED -> DELETING | ||
| containerManager.updateContainerState(cid, HddsProtos.LifeCycleEvent.DELETE); | ||
| containerManager.updateContainerState(ecCid, HddsProtos.LifeCycleEvent.DELETE); | ||
| assertEquals(LifeCycleState.DELETING, containerManager.getContainer(cid).getState()); | ||
| assertEquals(LifeCycleState.DELETING, containerManager.getContainer(ecCid).getState()); | ||
|
|
||
| // DELETING -> CLOSED | ||
| containerManager.transitionDeletingToClosedState(cid); | ||
| containerManager.transitionDeletingToClosedState(ecCid); | ||
| // the containers should be back in CLOSED state now | ||
| assertEquals(LifeCycleState.CLOSED, containerManager.getContainer(cid).getState()); | ||
| assertEquals(LifeCycleState.CLOSED, containerManager.getContainer(ecCid).getState()); | ||
| } | ||
|
|
||
| @Test | ||
| void testTransitionDeletingToClosedStateAllowsOnlyDeletingContainers() throws IOException { | ||
| // test for RATIS container | ||
| final ContainerInfo container = containerManager.allocateContainer( | ||
| RatisReplicationConfig.getInstance( | ||
| ReplicationFactor.THREE), "admin"); | ||
| final ContainerID cid = container.containerID(); | ||
| assertEquals(LifeCycleState.OPEN, containerManager.getContainer(cid).getState()); | ||
| assertThrows(IOException.class, () -> containerManager.transitionDeletingToClosedState(cid)); | ||
|
|
||
| // test for EC container | ||
| final ContainerInfo ecContainer = containerManager.allocateContainer(new ECReplicationConfig(3, 2), "admin"); | ||
| final ContainerID ecCid = ecContainer.containerID(); | ||
| assertEquals(LifeCycleState.OPEN, containerManager.getContainer(ecCid).getState()); | ||
| assertThrows(IOException.class, () -> containerManager.transitionDeletingToClosedState(ecCid)); | ||
| } | ||
|
|
||
| @Test | ||
| void testGetContainers() throws Exception { | ||
| assertEquals(emptyList(), containerManager.getContainers()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,7 @@ | |
| import org.junit.jupiter.api.io.TempDir; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||
| import static org.mockito.Mockito.mock; | ||
| import static org.mockito.Mockito.any; | ||
| import static org.mockito.Mockito.when; | ||
|
|
@@ -148,6 +149,42 @@ public void checkReplicationStateMissingReplica() | |
| assertEquals(3, c1.getReplicationConfig().getRequiredNodes()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testTransitionDeletingToClosedState() throws IOException { | ||
| HddsProtos.ContainerInfoProto.Builder builder = HddsProtos.ContainerInfoProto.newBuilder(); | ||
| builder.setContainerID(1) | ||
| .setState(HddsProtos.LifeCycleState.DELETING) | ||
| .setUsedBytes(0) | ||
| .setNumberOfKeys(0) | ||
| .setOwner("root") | ||
| .setReplicationType(HddsProtos.ReplicationType.RATIS) | ||
| .setReplicationFactor(ReplicationFactor.THREE); | ||
|
|
||
| HddsProtos.ContainerInfoProto container = builder.build(); | ||
| HddsProtos.ContainerID cid = HddsProtos.ContainerID.newBuilder().setId(container.getContainerID()).build(); | ||
| containerStateManager.addContainer(container); | ||
| containerStateManager.transitionDeletingToClosedState(cid); | ||
| assertEquals(HddsProtos.LifeCycleState.CLOSED, containerStateManager.getContainer(ContainerID.getFromProtobuf(cid)) | ||
| .getState()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testTransitionDeletingToClosedStateAllowsOnlyDeletingContainer() throws IOException { | ||
| HddsProtos.ContainerInfoProto.Builder builder = HddsProtos.ContainerInfoProto.newBuilder(); | ||
| builder.setContainerID(1) | ||
| .setState(HddsProtos.LifeCycleState.QUASI_CLOSED) | ||
| .setUsedBytes(0) | ||
| .setNumberOfKeys(0) | ||
| .setOwner("root") | ||
| .setReplicationType(HddsProtos.ReplicationType.RATIS) | ||
| .setReplicationFactor(ReplicationFactor.THREE); | ||
|
|
||
| HddsProtos.ContainerInfoProto container = builder.build(); | ||
| HddsProtos.ContainerID cid = HddsProtos.ContainerID.newBuilder().setId(container.getContainerID()).build(); | ||
| containerStateManager.addContainer(container); | ||
| assertThrows(IOException.class, () -> containerStateManager.transitionDeletingToClosedState(cid)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can test for the more specific |
||
| } | ||
|
|
||
| private void addReplica(ContainerInfo cont, DatanodeDetails node) { | ||
| ContainerReplica replica = ContainerReplica.newBuilder() | ||
| .setContainerID(cont.containerID()) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.