Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8ba9e8d
HDDS-12421. ContainerReportHandler should not take the call to delete…
smengcl Feb 26, 2025
ff0f98f
If a DELETING or DELETED container has a non-empty replica, the conta…
siddhantsangwan Feb 27, 2025
ea69cb0
Revert "If a DELETING or DELETED container has a non-empty replica, t…
smengcl Feb 27, 2025
818e889
Address Ethan's comments:
smengcl Feb 27, 2025
aa76bc8
Checkstyle
smengcl Feb 27, 2025
f205a6d
Add test case for container state DELETED -> CLOSED transition.
smengcl Feb 27, 2025
647a400
Add test case for DELETED -> CLOSED in `TestContainerStateManager`, `…
smengcl Feb 27, 2025
dc9ac6b
Consider replica states OPEN, CLOSING, CLOSED, QUASI_CLOSED and UNHEA…
siddhantsangwan Feb 28, 2025
1bc4181
modify tests in TestContainerReportHandler
siddhantsangwan Feb 28, 2025
e5962e4
remove unused import
siddhantsangwan Feb 28, 2025
f1971c4
Address comments.
smengcl Feb 28, 2025
08d11c9
UT: Check if delete command is issued or not as expected at the end.
smengcl Mar 1, 2025
65a2876
Fix existing UT `ecContainerShouldTransitionFromDeletingToClosedWhenN…
smengcl Mar 1, 2025
fb77bae
Fix existing UT `testStaleReplicaOfDeletedContainer`, and added case …
smengcl Mar 1, 2025
c26e3df
Checkstyle
smengcl Mar 1, 2025
e854276
Fix `testResendDeleteCommand` (because with HDDS-12421, only empty co…
smengcl Mar 3, 2025
a132b80
Merge UT `ratisContainerShouldTransitionFromDeletingOrDeletedToClosed…
smengcl Mar 3, 2025
448a568
force delete for `UNKNOWN_CONTAINER_ACTION_DELETE`.
smengcl Mar 3, 2025
c7717d8
Merge remote-tracking branch 'asf/master' into HDDS-12421-crh-dont-de…
smengcl Mar 4, 2025
a027755
Fix `WhenEmptyReplica` test case, include EC test case parameterizati…
smengcl Mar 4, 2025
d1f96a5
Fix javadoc.
smengcl Mar 4, 2025
99a6601
empty test
swamirishi Mar 6, 2025
c94aff8
empty test
swamirishi Mar 6, 2025
f96df52
empty test
swamirishi Mar 7, 2025
849eb62
HDDS-12421. Remove ignored flag & restructure switch case
swamirishi Mar 6, 2025
e710734
HDDS-12421. Fix test case
swamirishi Mar 7, 2025
b221882
add test case to assert that DeletingContainerHandler does not send d…
siddhantsangwan Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -352,26 +352,22 @@ private boolean updateContainerState(final DatanodeDetails datanode,
*/
break;
case DELETING:
case DELETED:
/*
HDDS-11136: If a DELETING container has a non-empty CLOSED replica, the container should be moved back to CLOSED
state.
* HDDS-11136: If a DELETING container has a non-empty CLOSED replica, the container should be moved back to
* CLOSED state.
*
* HDDS-12421: If a DELETED container has a non-empty CLOSED replica, the container should also be moved back to
* CLOSED state.
*/
boolean replicaStateAllowed = replica.getState() == State.CLOSED;
boolean replicaNotEmpty = replica.hasIsEmpty() && !replica.getIsEmpty();
Copy link
Contributor Author

@smengcl smengcl Feb 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@siddhantsangwan I have a question, do we have cases where replica.hasIsEmpty() == false ?

Right now, replicaIsEmpty would be false when the replica doesn't have the isEmpty field. So !replicaIsEmpty becomes true below and we effectively treat such replicas as non-empty (which is fine).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks to me like it is ok to omit the existence check for the field, but Siddhant should confirm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a question, do we have cases where replica.hasIsEmpty() == false ?

I don't know either.

Right now, replicaIsEmpty would be false when the replica doesn't have the isEmpty field. So !replicaIsEmpty becomes true below and we effectively treat such replicas as non-empty (which is fine).

This behaviour seems fine to me, we shouldn't be deleting replicas if the isEmpty field is not present at all. Let's keep the current behaviour as it is.

if (replicaStateAllowed && replicaNotEmpty) {
logger.info("Moving DELETING container {} to CLOSED state, datanode {} reported replica with state={}, " +
"isEmpty={} and keyCount={}.", containerId, datanode.getHostName(), replica.getState(), false,
replica.getKeyCount());
containerManager.transitionDeletingToClosedState(containerId);
logger.info("Moving container {} from {} to CLOSED state, datanode {} reported replica with state={}, " +
"isEmpty={} and keyCount={}.",
replica.getState(), containerId, datanode.getHostName(), replica.getState(), false, replica.getKeyCount());
containerManager.transitionDeletingOrDeletedToClosedState(containerId);
}

break;
case DELETED:
/*
* The container is deleted. delete the replica.
*/
deleteReplica(containerId, datanode, publisher, "DELETED");
ignored = true;
break;
default:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,14 @@ void updateContainerState(ContainerID containerID,
throws IOException, InvalidStateTransitionException;

/**
* Bypasses the container state machine to change a container's state from DELETING to CLOSED. This API was
* Bypasses the container state machine to change a container's state from DELETING or DELETED to CLOSED. This API was
* introduced to fix a bug (HDDS-11136), and should be used with care otherwise.
*
* @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;
void transitionDeletingOrDeletedToClosedState(ContainerID containerID) throws IOException;

/**
* Returns the latest list of replicas for given containerId.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,12 @@ public void updateContainerState(final ContainerID cid,
}

@Override
public void transitionDeletingToClosedState(ContainerID containerID) throws IOException {
public void transitionDeletingOrDeletedToClosedState(ContainerID containerID) throws IOException {
HddsProtos.ContainerID proto = containerID.getProtobuf();
lock.lock();
try {
if (containerExist(containerID)) {
containerStateManager.transitionDeletingToClosedState(proto);
containerStateManager.transitionDeletingOrDeletedToClosedState(proto);
} else {
throwContainerNotFoundException(containerID);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,15 @@ void updateContainerState(HddsProtos.ContainerID id,


/**
* Bypasses the container state machine to change a container's state from DELETING to CLOSED. This API was
* Bypasses the container state machine to change a container's state from DELETING or DELETED to CLOSED. This API was
* introduced to fix a bug (HDDS-11136), and should be used with care otherwise.
*
* @see <a href="https://issues.apache.org/jira/browse/HDDS-11136">HDDS-11136</a>
* @param id id of the container to transition
* @throws IOException
*/
@Replicate
void transitionDeletingToClosedState(HddsProtos.ContainerID id) throws IOException;
void transitionDeletingOrDeletedToClosedState(HddsProtos.ContainerID id) throws IOException;

/**
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,14 +370,14 @@ public void updateContainerState(final HddsProtos.ContainerID containerID,
}

@Override
public void transitionDeletingToClosedState(HddsProtos.ContainerID containerID) throws IOException {
public void transitionDeletingOrDeletedToClosedState(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) {
if (oldState != DELETING && oldState != DELETED) {
throw new InvalidContainerStateException("Cannot transition container " + id + " from " + oldState +
" back to CLOSED. The container must be in the DELETING state.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ void testTransitionDeletingToClosedState() throws IOException, InvalidStateTrans
assertEquals(LifeCycleState.DELETING, containerManager.getContainer(ecCid).getState());

// DELETING -> CLOSED
containerManager.transitionDeletingToClosedState(cid);
containerManager.transitionDeletingToClosedState(ecCid);
containerManager.transitionDeletingOrDeletedToClosedState(cid);
containerManager.transitionDeletingOrDeletedToClosedState(ecCid);
// the containers should be back in CLOSED state now
assertEquals(LifeCycleState.CLOSED, containerManager.getContainer(cid).getState());
assertEquals(LifeCycleState.CLOSED, containerManager.getContainer(ecCid).getState());
Expand All @@ -178,13 +178,13 @@ void testTransitionDeletingToClosedStateAllowsOnlyDeletingContainers() throws IO
ReplicationFactor.THREE), "admin");
final ContainerID cid = container.containerID();
assertEquals(LifeCycleState.OPEN, containerManager.getContainer(cid).getState());
assertThrows(IOException.class, () -> containerManager.transitionDeletingToClosedState(cid));
assertThrows(IOException.class, () -> containerManager.transitionDeletingOrDeletedToClosedState(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));
assertThrows(IOException.class, () -> containerManager.transitionDeletingOrDeletedToClosedState(ecCid));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ void setup() throws IOException, InvalidStateTransitionException {
any(ContainerID.class), any(ContainerReplica.class));

doAnswer(invocation -> {
containerStateManager.transitionDeletingToClosedState(((ContainerID) invocation.getArgument(0)).getProtobuf());
containerStateManager.transitionDeletingOrDeletedToClosedState(((ContainerID) invocation.getArgument(0)).getProtobuf());
return null;
}).when(containerManager).transitionDeletingToClosedState(any(ContainerID.class));
}).when(containerManager).transitionDeletingOrDeletedToClosedState(any(ContainerID.class));
}

@AfterEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ public void testTransitionDeletingToClosedState() throws IOException {
HddsProtos.ContainerInfoProto container = builder.build();
HddsProtos.ContainerID cid = HddsProtos.ContainerID.newBuilder().setId(container.getContainerID()).build();
containerStateManager.addContainer(container);
containerStateManager.transitionDeletingToClosedState(cid);
containerStateManager.transitionDeletingOrDeletedToClosedState(cid);
assertEquals(HddsProtos.LifeCycleState.CLOSED, containerStateManager.getContainer(ContainerID.getFromProtobuf(cid))
.getState());
}
Expand All @@ -181,7 +181,7 @@ public void testTransitionDeletingToClosedStateAllowsOnlyDeletingContainer() thr
HddsProtos.ContainerID cid = HddsProtos.ContainerID.newBuilder().setId(container.getContainerID()).build();
containerStateManager.addContainer(container);
try {
containerStateManager.transitionDeletingToClosedState(cid);
containerStateManager.transitionDeletingOrDeletedToClosedState(cid);
fail("Was expecting an Exception, but did not catch any.");
} catch (IOException e) {
assertInstanceOf(InvalidContainerStateException.class, e.getCause().getCause());
Expand Down
Loading