-
Notifications
You must be signed in to change notification settings - Fork 616
HDDS-14926. Allow QUASI_CLOSED containers in DiskBalancer with improved debug logs for containers #10022
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-14926. Allow QUASI_CLOSED containers in DiskBalancer with improved debug logs for containers #10022
Changes from all commits
cb501b5
5a58593
4a50f7e
90c6d07
10d1c7b
78e625a
0b58822
1d26115
538abc4
21d9e69
ab0ae6f
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 |
|---|---|---|
|
|
@@ -21,10 +21,18 @@ | |
|
|
||
| import jakarta.annotation.Nonnull; | ||
| import java.time.Duration; | ||
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.HashSet; | ||
| import java.util.Locale; | ||
| import java.util.Set; | ||
| import org.apache.commons.lang3.StringUtils; | ||
| import org.apache.hadoop.hdds.HddsUtils; | ||
| import org.apache.hadoop.hdds.conf.Config; | ||
| import org.apache.hadoop.hdds.conf.ConfigGroup; | ||
| import org.apache.hadoop.hdds.conf.ConfigTag; | ||
| import org.apache.hadoop.hdds.conf.ConfigType; | ||
| import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto.State; | ||
| import org.apache.hadoop.hdds.protocol.proto.HddsProtos; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
@@ -37,6 +45,12 @@ public final class DiskBalancerConfiguration { | |
| private static final Logger LOG = | ||
| LoggerFactory.getLogger(DiskBalancerConfiguration.class); | ||
|
|
||
| /** | ||
| * Default value for {@code hdds.datanode.disk.balancer.container.states}: {@value} | ||
| * (CLOSED and QUASI_CLOSED containers may be disk-balanced). | ||
| */ | ||
| public static final String DEFAULT_CONTAINER_STATES = "CLOSED,QUASI_CLOSED"; | ||
|
|
||
| @Config(key = "hdds.datanode.disk.balancer.info.dir", type = ConfigType.STRING, | ||
| defaultValue = "", tags = {ConfigTag.DISKBALANCER}, | ||
| description = "The path where datanode diskBalancer's conf is to be " + | ||
|
|
@@ -121,6 +135,19 @@ public final class DiskBalancerConfiguration { | |
| "Unit could be defined with postfix (ns,ms,s,m,h,d).") | ||
| private long replicaDeletionDelay = Duration.ofMinutes(5).toMillis(); | ||
|
|
||
| private static final String HDDS_DATANODE_DISK_BALANCER_CONTAINER_STATES = | ||
| "hdds.datanode.disk.balancer.container.states"; | ||
|
|
||
| @Config(key = HDDS_DATANODE_DISK_BALANCER_CONTAINER_STATES, | ||
| defaultValue = DEFAULT_CONTAINER_STATES, | ||
| type = ConfigType.STRING, | ||
| tags = { DATANODE, ConfigTag.DISKBALANCER }, | ||
| description = "Container lifecycle state names (CLOSED, QUASI_CLOSED) that " + | ||
| "are eligible to be moved between disks on a datanode. " + | ||
| "Names must match the enum exactly (uppercase). The default includes CLOSED and QUASI_CLOSED; " + | ||
| "additional states can be enabled by adding the states here.") | ||
| private String containerStates = DEFAULT_CONTAINER_STATES; | ||
|
|
||
| public DiskBalancerConfiguration(Double threshold, | ||
| Long bandwidthInMB, | ||
| Integer parallelThread, | ||
|
|
@@ -262,17 +289,102 @@ public void setParallelThread(int parallelThread) { | |
| this.parallelThread = parallelThread; | ||
| } | ||
|
|
||
| public String getContainerStates() { | ||
| return containerStates; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the comma-separated container state names and validates them immediately. | ||
| * <p> | ||
| * Ozone configuration injection may set the raw container-states string without invoking this | ||
| * setter; {@link #getMovableContainerStates()} still parses the current field value on read. | ||
| * | ||
| * @param containerStates comma-separated {@link State} names (case-sensitive, uppercase); | ||
| * whitespace allowed around commas and tokens | ||
| * @throws IllegalArgumentException if blank, empty after parsing tokens, or unknown state | ||
| */ | ||
| public void setContainerStates(String containerStates) { | ||
| parseMovableContainerStates(containerStates); | ||
| this.containerStates = containerStates; | ||
| } | ||
|
|
||
| /** | ||
| * Container lifecycle states eligible for disk balancing, derived from {@link #getContainerStates()}: | ||
| * same rules as {@link #setContainerStates(String)}. | ||
| * | ||
| * @return unmodifiable non-empty set | ||
| */ | ||
| public Set<State> getMovableContainerStates() { | ||
|
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. Please make it private, call it from setContainerStates. We should verify the new setting value in setContainerStates.
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.
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 verifying the new setting value in It doesn’t: for If we made that method private and only called it from
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. Can we do the parseMovableContainerStates in the constructor?
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. As far as I understand the contsructor cannot validate because |
||
| return parseMovableContainerStates(this.containerStates); | ||
| } | ||
|
|
||
| /** | ||
| * Parses and validates {@code hdds.datanode.disk.balancer.container.states}. | ||
| */ | ||
| private static Set<State> parseMovableContainerStates(String raw) { | ||
| if (StringUtils.isBlank(raw)) { | ||
| throw new IllegalArgumentException(HDDS_DATANODE_DISK_BALANCER_CONTAINER_STATES + " must not be empty."); | ||
| } | ||
| Set<State> states = new HashSet<>(); | ||
| for (String part : raw.split(",")) { | ||
| String name = part.trim(); | ||
| if (name.isEmpty()) { | ||
| continue; | ||
| } | ||
| State state; | ||
| try { | ||
| state = State.valueOf(name); | ||
| } catch (IllegalArgumentException ex) { | ||
| if (wouldMatchContainerStateIfUppercased(name)) { | ||
| throw new IllegalArgumentException( | ||
| "Invalid container state '" + name + "': use uppercase enum names " | ||
| + "(e.g. CLOSED, QUASI_CLOSED). Valid names are: " | ||
| + Arrays.toString(State.values()), | ||
| ex); | ||
| } | ||
| throw new IllegalArgumentException( | ||
| "Invalid container state '" + name + "' in " + HDDS_DATANODE_DISK_BALANCER_CONTAINER_STATES + ". " | ||
| + "Valid names are: " + Arrays.toString(State.values()), | ||
| ex); | ||
| } | ||
| if (HddsUtils.isOpenToWriteState(state) || state == State.DELETED) { | ||
| throw new IllegalArgumentException("State " + name + " is not movable."); | ||
| } | ||
| states.add(State.valueOf(name)); | ||
| } | ||
| if (states.isEmpty()) { | ||
| throw new IllegalArgumentException( | ||
| HDDS_DATANODE_DISK_BALANCER_CONTAINER_STATES + " must list at least one valid state."); | ||
| } | ||
| return Collections.unmodifiableSet(states); | ||
| } | ||
|
|
||
| private static boolean wouldMatchContainerStateIfUppercased(String name) { | ||
| String upper = name.toUpperCase(Locale.ROOT); | ||
| if (upper.equals(name)) { | ||
| return false; | ||
| } | ||
| try { | ||
| State.valueOf(upper); | ||
| return true; | ||
| } catch (IllegalArgumentException e) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return String.format("Disk Balancer Configuration values:%n" + | ||
| "%-50s %s%n" + | ||
| "%-50s %s%n" + | ||
| "%-50s %s%n" + | ||
| "%-50s %s%n" + | ||
| "%-50s %s%n" + | ||
| "%-50s %s%n", | ||
| "Key", "Value", | ||
| "Threshold", threshold, "Max disk bandwidth", diskBandwidthInMB, | ||
| "Parallel Thread", parallelThread, "Stop After Disk Even", stopAfterDiskEven); | ||
| "Parallel Thread", parallelThread, "Stop After Disk Even", stopAfterDiskEven, | ||
| "Container states", containerStates); | ||
| } | ||
|
|
||
| public HddsProtos.DiskBalancerConfigurationProto.Builder toProtobufBuilder() { | ||
|
|
@@ -282,7 +394,8 @@ public HddsProtos.DiskBalancerConfigurationProto.Builder toProtobufBuilder() { | |
| builder.setThreshold(threshold) | ||
| .setDiskBandwidthInMB(diskBandwidthInMB) | ||
| .setParallelThread(parallelThread) | ||
| .setStopAfterDiskEven(stopAfterDiskEven); | ||
| .setStopAfterDiskEven(stopAfterDiskEven) | ||
| .setContainerStates(containerStates); | ||
| return builder; | ||
| } | ||
|
|
||
|
|
@@ -309,6 +422,9 @@ public static DiskBalancerConfiguration updateFromProtobuf( | |
| if (newConfigProto.hasStopAfterDiskEven()) { | ||
| existingConfig.setStopAfterDiskEven(newConfigProto.getStopAfterDiskEven()); | ||
| } | ||
| if (newConfigProto.hasContainerStates()) { | ||
| existingConfig.setContainerStates(newConfigProto.getContainerStates()); | ||
| } | ||
| return existingConfig; | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.