-
Notifications
You must be signed in to change notification settings - Fork 623
HDDS-11389. Incorrect number of deleted containers shown in Recon UI. #7149
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
Merged
Merged
Changes from 5 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
647806f
HDDS-11389. Incorrect number of deleted containers shown in Recon UI.
ArafatKhan2198 adb768c
Fixed the problem of duplicate values being added to unhealthyContain…
ArafatKhan2198 346ae0d
Removed the addition of EmptyMissind and NegativeContainers from the …
ArafatKhan2198 6941dc2
Fixed the code and added new tests and updated existing ones
ArafatKhan2198 8fcb72c
Fixed checkstyle issues
ArafatKhan2198 4959cca
Placed the Log after delete
ArafatKhan2198 460a94c
Fixed findBugs issue
ArafatKhan2198 212bf24
Fixed a comment
ArafatKhan2198 dd19857
Fixed a bug
ArafatKhan2198 ceff089
Made final review comments
ArafatKhan2198 bcd6754
Fixed final review comments
ArafatKhan2198 14ffa8e
Removed unnecessary commit
ArafatKhan2198 65e2911
Improved Integration test
ArafatKhan2198 5603df6
Fixed checkstyle issues
ArafatKhan2198 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,7 @@ | |
| import java.util.concurrent.locks.ReadWriteLock; | ||
| import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
|
|
||
| import com.google.common.annotations.VisibleForTesting; | ||
| import org.apache.hadoop.hdds.conf.OzoneConfiguration; | ||
| import org.apache.hadoop.hdds.protocol.proto.HddsProtos; | ||
| import org.apache.hadoop.hdds.scm.PlacementPolicy; | ||
|
|
@@ -188,7 +189,7 @@ private void logUnhealthyContainerStats( | |
| // If any EMPTY_MISSING containers, then it is possible that such | ||
| // containers got stuck in the closing state which never got | ||
| // any replicas created on the datanodes. In this case, we log it as | ||
| // EMPTY, and insert as EMPTY_MISSING in UNHEALTHY_CONTAINERS table. | ||
| // EMPTY_MISSING in unhealthy container statistics but do not add it to the table. | ||
| unhealthyContainerStateStatsMap.entrySet().forEach(stateEntry -> { | ||
| UnHealthyContainerStates unhealthyContainerState = stateEntry.getKey(); | ||
| Map<String, Long> containerStateStatsMap = stateEntry.getValue(); | ||
|
|
@@ -256,6 +257,11 @@ private void completeProcessingContainer( | |
| * completeProcessingContainer is called. This will check to see if any | ||
| * additional records need to be added to the database. | ||
| * | ||
| * If a container is identified as missing, empty-missing, under-replicated, | ||
| * over-replicated or mis-replicated, the method checks with SCM to determine | ||
| * if it has been deleted, using {@code containerDeletedInSCM}. If the container is | ||
| * deleted in SCM, the corresponding record is removed from Recon. | ||
| * | ||
| * @param currentTime Timestamp to place on all records generated by this run | ||
| * @param unhealthyContainerStateCountMap | ||
| * @return Count of records processed | ||
|
|
@@ -273,34 +279,41 @@ private long processExistingDBRecords(long currentTime, | |
| recordCount++; | ||
| UnhealthyContainersRecord rec = cursor.fetchNext(); | ||
| try { | ||
| // Set the current container if it's not already set | ||
| if (currentContainer == null) { | ||
| currentContainer = setCurrentContainer(rec.getContainerId()); | ||
| } | ||
| // If the container ID has changed, finish processing the previous one | ||
| if (currentContainer.getContainerID() != rec.getContainerId()) { | ||
| completeProcessingContainer( | ||
| currentContainer, existingRecords, currentTime, | ||
| unhealthyContainerStateCountMap); | ||
| existingRecords.clear(); | ||
| currentContainer = setCurrentContainer(rec.getContainerId()); | ||
| } | ||
| if (ContainerHealthRecords | ||
| .retainOrUpdateRecord(currentContainer, rec | ||
| )) { | ||
| // Check if the missing container is deleted in SCM | ||
| if (currentContainer.isMissing() && | ||
| containerDeletedInSCM(currentContainer.getContainer())) { | ||
| rec.delete(); | ||
| } | ||
| existingRecords.add(rec.getContainerState()); | ||
| if (rec.changed()) { | ||
| rec.update(); | ||
| } | ||
| } else { | ||
|
|
||
| // Unhealthy Containers such as MISSING, EMPTY_MISSING, UNDER_REPLICATED, | ||
| // OVER_REPLICATED, MIS_REPLICATED can have their unhealthy states changed or retained. | ||
| if (!ContainerHealthRecords.retainOrUpdateRecord(currentContainer, rec)) { | ||
| rec.delete(); | ||
| LOG.info("DELETED existing unhealthy container record...for Container: {}", | ||
|
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. Move this log after
Contributor
Author
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. Done! |
||
| currentContainer.getContainerID()); | ||
| } | ||
|
|
||
| // If the container is marked as MISSING and it's deleted in SCM, remove the record | ||
| if (currentContainer.isMissing() && containerDeletedInSCM(currentContainer.getContainer())) { | ||
| LOG.info("DELETED existing unhealthy container record...for Container: {}", | ||
| currentContainer.getContainerID()); | ||
| rec.delete(); | ||
| } | ||
|
|
||
| existingRecords.add(rec.getContainerState()); | ||
| // If the record was changed, update it | ||
| if (rec.changed()) { | ||
|
ArafatKhan2198 marked this conversation as resolved.
|
||
| rec.update(); | ||
| } | ||
| } catch (ContainerNotFoundException cnf) { | ||
| // If the container is not found, delete the record and reset currentContainer | ||
| rec.delete(); | ||
| currentContainer = null; | ||
| } | ||
|
|
@@ -349,6 +362,18 @@ private void processContainer(ContainerInfo container, long currentTime, | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Ensures the container's state in Recon is updated to match its state in SCM. | ||
| * | ||
| * If SCM reports the container as DELETED, this method attempts to transition | ||
| * the container's state in Recon from CLOSED to DELETING, or from DELETING to | ||
| * DELETED, based on the current state in Recon. It logs each transition attempt | ||
| * and handles any exceptions that may occur. | ||
| * | ||
| * @param containerInfo the container whose state is being checked and potentially updated. | ||
| * @return {@code true} if the container was found to be DELETED in SCM and the | ||
| * state transition was attempted in Recon; {@code false} otherwise. | ||
| */ | ||
| private boolean containerDeletedInSCM(ContainerInfo containerInfo) { | ||
| try { | ||
| ContainerWithPipeline containerWithPipeline = | ||
|
|
@@ -358,13 +383,16 @@ private boolean containerDeletedInSCM(ContainerInfo containerInfo) { | |
| if (containerInfo.getState() == HddsProtos.LifeCycleState.CLOSED) { | ||
| containerManager.updateContainerState(containerInfo.containerID(), | ||
| HddsProtos.LifeCycleEvent.DELETE); | ||
| LOG.debug("Successfully changed container {} state from CLOSED to DELETING.", | ||
| containerInfo.containerID()); | ||
| } | ||
| if (containerInfo.getState() == HddsProtos.LifeCycleState.DELETING && | ||
| containerManager.getContainerReplicas(containerInfo.containerID()) | ||
| .size() == 0 | ||
| ) { | ||
| containerManager.updateContainerState(containerInfo.containerID(), | ||
| HddsProtos.LifeCycleEvent.CLEANUP); | ||
| LOG.info("Successfully Deleted container {} from Recon.", containerInfo.containerID()); | ||
| } | ||
| return true; | ||
| } | ||
|
|
@@ -401,7 +429,6 @@ private void handleNegativeSizedContainers( | |
| populateContainerStats(containerHealthStatus, | ||
| UnHealthyContainerStates.NEGATIVE_SIZE, | ||
| unhealthyContainerStateStatsMap); | ||
| containerHealthSchemaManager.insertUnhealthyContainerRecords(records); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -493,13 +520,17 @@ public static List<UnhealthyContainers> generateUnhealthyRecords( | |
| unhealthyContainerStateStatsMap); | ||
| } else { | ||
|
|
||
| LOG.debug("Empty container {} is missing. Kindly check the " + | ||
| "consolidated container stats per UNHEALTHY state logged as " + | ||
| "starting with **Container State Stats:**"); | ||
| // EMPTY_MISSING containers are not inserted into the database. | ||
| // These containers typically represent those that were never written to | ||
| // or remain in an incomplete state. Tracking such containers as unhealthy | ||
| // would not provide valuable insights since they don't pose a risk or issue | ||
| // to the system. Instead, they are logged for awareness, but not stored in | ||
| // the UNHEALTHY_CONTAINERS table to avoid unnecessary entries. | ||
|
|
||
| LOG.debug("Empty container {} is missing. It will be logged in the " + | ||
| "unhealthy container statistics, but no record will be created in the " + | ||
| "UNHEALTHY_CONTAINERS table.", container.getContainerID()); | ||
|
|
||
| records.add( | ||
| recordForState(container, EMPTY_MISSING, | ||
| time)); | ||
| populateContainerStats(container, | ||
| EMPTY_MISSING, | ||
| unhealthyContainerStateStatsMap); | ||
|
|
@@ -508,6 +539,22 @@ public static List<UnhealthyContainers> generateUnhealthyRecords( | |
| return records; | ||
| } | ||
|
|
||
| // For Negative sized containers we only log but not insert into DB | ||
| if (container.getContainer().getUsedBytes() < 0 | ||
| && !recordForStateExists.contains( | ||
| UnHealthyContainerStates.NEGATIVE_SIZE.toString())) { | ||
| // NEGATIVE_SIZE containers are also not inserted into the database. | ||
| // This condition usually arises due to corrupted or invalid metadata, where | ||
| // the container's size is inaccurately recorded as negative. Since this does not | ||
| // represent a typical unhealthy scenario and may not have any meaningful | ||
| // impact on system health, such containers are logged for investigation but | ||
| // excluded from the UNHEALTHY_CONTAINERS table to maintain data integrity. | ||
| populateContainerStats(container, | ||
| UnHealthyContainerStates.NEGATIVE_SIZE, | ||
| unhealthyContainerStateStatsMap); | ||
| return records; | ||
| } | ||
|
|
||
| if (container.isUnderReplicated() | ||
| && !recordForStateExists.contains( | ||
| UnHealthyContainerStates.UNDER_REPLICATED.toString())) { | ||
|
|
@@ -650,4 +697,15 @@ private static void populateContainerStats( | |
| (value + container.getContainer().getUsedBytes())); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Expose the logger for testing purposes. | ||
| * | ||
| * @return the logger instance | ||
| */ | ||
| @VisibleForTesting | ||
| public Logger getLogger() { | ||
| return LOG; | ||
| } | ||
|
|
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.