Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b432c98
Commit new workflow def for repeated test of HDDS-5358.
neils-dev Jul 27, 2021
48f27b0
Merge remote-tracking branch 'upstream/master'
Sep 8, 2022
110c612
Initial commit for Decommissioning progress monitoring metrics for JM…
Sep 16, 2022
8593546
Integrated decommission progress metrics with monitor. Added unit te…
Sep 21, 2022
526cd84
Fixed metric collection for node_decommission_metrics_total_tracked_d…
Sep 24, 2022
3faec5b
Cleanup and added code documentation. Minor bug fix for collecting m…
Sep 27, 2022
f4c9eb5
Trigger build
Sep 27, 2022
5a22113
Added additional metrics for monitoring progress of decommission and …
Oct 14, 2022
ea64bfa
modified getMetrics method in NodeDecommissionMetrics for stability -…
Oct 17, 2022
a8de744
Minor fix to inner class in monitor code changing from private final …
Oct 18, 2022
a24ad0f
Modifications made to NodeDecommissionMetrics to collect decommission…
Oct 22, 2022
b13aab0
Merge branch 'master' into metrics
neils-dev Oct 22, 2022
1232867
Update package path of RatisContainerReplicaCount in DatanodeAdminMon…
Oct 24, 2022
6d6b324
Modifications made to monitor node metrics to use threadLocal variabl…
neils-dev Oct 27, 2022
536773b
Revert "Modifications made to monitor node metrics to use threadLocal…
neils-dev Oct 28, 2022
efc2554
Added locking support for monitoring setting of snapshot to metrics o…
neils-dev Oct 28, 2022
3a0efd5
Cleanup of reset in monitor metrics collection. Changed node decomm/…
Oct 29, 2022
561c368
Simplification of NodeDecommisionMetrics, using ContainerStateInWorkf…
neils-dev Nov 8, 2022
75ad487
Minor changes to remove prefix tracked from NodeDecommissionMetrics p…
neils-dev Nov 8, 2022
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 @@ -30,5 +30,5 @@ public interface DatanodeAdminMonitor extends Runnable {
void startMonitoring(DatanodeDetails dn);
void stopMonitoring(DatanodeDetails dn);
Set<DatanodeDetails> getTrackedNodes();

void setMetrics(NodeDecommissionMetrics metrics);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package org.apache.hadoop.hdds.scm.node;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.DatanodeDetails;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
Expand All @@ -38,8 +39,10 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -74,6 +77,51 @@ public class DatanodeAdminMonitorImpl implements DatanodeAdminMonitor {
private Queue<DatanodeDetails> pendingNodes = new ArrayDeque();
private Queue<DatanodeDetails> cancelledNodes = new ArrayDeque();
private Set<DatanodeDetails> trackedNodes = new HashSet<>();
private NodeDecommissionMetrics metrics;
private long pipelinesWaitingToClose = 0;
private long sufficientlyReplicatedContainers = 0;
private long trackedDecomMaintenance = 0;
private long trackedRecommission = 0;
private long unhealthyContainers = 0;
private long underReplicatedContainers = 0;

@SuppressFBWarnings(value = "SIC_INNER_SHOULD_BE_STATIC")
private final class ContainerStateInWorkflow {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rather than suppress the FB warning, can this class be made private final static class ...? I am not an expert in this area, but usually inner classes I've seen are static. The difference between static and non static inner classes seems to be that "non static" inner classes can directly access the enclosing classes instance variables and methods.

A static inner class cannot directly access the enclosing classes methods. It has to do it via an object reference.

In this case, the inner class is a simple wrapper around a set of variables and does not need to access the enclosing methods class, and therefore can be static I think.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. Removed the suppress annotation and properly converted instead to static final nested class from the final inner class. As the inner class does not refer to the outer class instance, it should indeed be a static nested class.

private long sufficientlyReplicated = 0;
private long unhealthyContainers = 0;
private long underReplicatedContainers = 0;
private String host = "";

private ContainerStateInWorkflow(String host,
long sufficientlyReplicated,
Comment thread
sodonnel marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we ever pass non-zeros for these values? Perhaps we can just drop these parameters and let them default to zero?

@neils-dev neils-dev Oct 25, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, currently we instantiate zeroing the values then use the setters to update the values. The constructor contains the parameters in case we start initializing with non-zero values. If we don't use it, we can remove and default the parameters to zero.

long underReplicatedContainers,
long unhealthyContainers) {
this.host = host;
this.sufficientlyReplicated = sufficientlyReplicated;
this.unhealthyContainers = unhealthyContainers;
this.underReplicatedContainers = underReplicatedContainers;
}

public void setAll(long sufficiently,
long under,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Formatting here seems off again - should either be 4 spaces in from the line above or aligned with the other parameters.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

long unhealthy) {
sufficientlyReplicated = sufficiently;
underReplicatedContainers = under;
unhealthyContainers = unhealthy;
}
public void reset() {
sufficientlyReplicated = 0L;
underReplicatedContainers = 0L;
unhealthyContainers = 0L;
}

public String getHost() {
return host;
}
}

private Map<String, ContainerStateInWorkflow> containerStateByHost;
private Map<String, Long> pipelinesWaitingToCloseByHost;

private static final Logger LOG =
LoggerFactory.getLogger(DatanodeAdminMonitorImpl.class);
Expand All @@ -90,6 +138,9 @@ public DatanodeAdminMonitorImpl(
this.eventQueue = eventQueue;
this.nodeManager = nodeManager;
this.replicationManager = replicationManager;

containerStateByHost = new HashMap<>();
pipelinesWaitingToCloseByHost = new HashMap<>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why split the pipelines into a seperate map? It looks like it would be easier overall to have a pipeline count setter on the ContainerStateInWorkflow object and just carry the pipeline count around with the containers etc too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Split between replication state and pipelines was for grouping - they are initialized and set in separate parts of the monitor code that resulted in using two separate maps to store the two. Looking to, as suggested, reuse the ContainerStateInWorkflow for the two, perhaps two different setters; one for the replication and the other for pipelines.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Combined all metrics collected by host in monitor to ContainerStateInWorkflow as suggested.

}

/**
Expand Down Expand Up @@ -117,6 +168,10 @@ public synchronized void stopMonitoring(DatanodeDetails dn) {
cancelledNodes.add(dn);
}

public synchronized void setMetrics(NodeDecommissionMetrics metrics) {
this.metrics = metrics;
}

/**
* Get the set of nodes which are currently tracked in the decommissioned
* and maintenance workflow.
Expand All @@ -140,15 +195,18 @@ public synchronized Set<DatanodeDetails> getTrackedNodes() {
public void run() {
try {
synchronized (this) {
trackedRecommission = getCancelledCount();
processCancelledNodes();
processPendingNodes();
trackedDecomMaintenance = getTrackedNodeCount();
}
processTransitioningNodes();
if (trackedNodes.size() > 0 || pendingNodes.size() > 0) {
LOG.info("There are {} nodes tracked for decommission and " +
"maintenance. {} pending nodes.",
trackedNodes.size(), pendingNodes.size());
}
setMetricsToGauge();
} catch (Exception e) {
LOG.error("Caught an error in the DatanodeAdminMonitor", e);
// Intentionally do not re-throw, as if we do the monitor thread
Expand All @@ -168,6 +226,43 @@ public int getTrackedNodeCount() {
return trackedNodes.size();
}

synchronized void setMetricsToGauge() {
metrics.setTrackedContainersUnhealthyTotal(unhealthyContainers);
metrics.setTrackedRecommissionNodesTotal(trackedRecommission);
metrics.setTrackedDecommissioningMaintenanceNodesTotal(
trackedDecomMaintenance);
metrics.setTrackedContainersUnderReplicatedTotal(
underReplicatedContainers);
metrics.setTrackedContainersSufficientlyReplicatedTotal(
sufficientlyReplicatedContainers);
metrics.setTrackedPipelinesWaitingToCloseTotal(pipelinesWaitingToClose);
for (Map.Entry<String, Long> e :
pipelinesWaitingToCloseByHost.entrySet()) {
metrics.metricRecordPipelineWaitingToCloseByHost(e.getKey(),
e.getValue());
}
for (Map.Entry<String, ContainerStateInWorkflow> e :

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I might be wrong, but I think there is a bug here.

Lets say we put a host to maintenance. It will have some metrics tracked in the ByHost maps.

After each pass we reset these maps to have zero counts, but we don't remove the entries from the maps anywhere (unless I have missed it). Then we update the values accordingly.

Later the node goes back into service and even though it is removed from the monitor, it will be tracked with zero counts forever.

Over time on a long running cluster, we will build up a lot of "by host" metrics with zero values, when they really should be removed.

I think the reset will need to remove them from the maps rather than zeroing them, and also when setting the values to the metric gauge, you will need to remove values no longer there from it too.

It might be easier to pass a Map<String, ContainerStateInWorkflow> to the metrics class to facilitate removing the stale entries.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@sodonnel , with the metrics registry it appears that the metrics we track remain in the registry. With this behavior, currently each datanode we add to track remains unless we have an api to remove it from the MetricsRegistry. Is there a way to delete/remove a gauge from the registry? See MetricsRegistry.java https://github.com/apache/hadoop/blob/03cfc852791c14fad39db4e5b14104a276c08e59/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/lib/MetricsRegistry.java#L40.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Huum, looks like you are correct. I wonder what the best approach is here.

I don't think its a great user experience if we start with no individual nodes track, and then over time (in a long running SCM) more and more nodes get added for maintenance and decommission and the number builds up all with zero counts. I guess its not a major problem, but it would be nice to resolve it somehow.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In #3791 Symious added a tag with a group of metrics in JSON form. For the metrics system, this is just a tag to string, rather than a gauge, but we could group all currently decommissioning / maintenence nodes into a JSON representation to expose the fine grained info. If no nodes are in the workflow, it would just be an empty json object, so nodes can come and go easily.

Then you still have your aggregate metrics as they are now.

It is unlikely that someone would want to chart an individual DN as they would have to create a new chart for each DN.

What do you think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've modified the code to dynamically (without using the helper MetricsRegistry class to add gauges) add to the collector as is done similarly in namenode topmetrics collections. See https://github.com/apache/hadoop/blob/eefa664fea1119a9c6e3ae2d2ad3069019fbd4ef/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/top/metrics/TopMetrics.java#L167.
Here the metrics are collected dynamically by host when the host node is in the workflow. When the node exits the workflow, the metrics for that host are no longer collected. In the JMX, the node metrics are no longer in the output. Note this is true for JMX, the prom endpoint seems to retain the last value pushed. See the following metrics pushed out to JMX for the NodeDecommissionMetrics when datanode-2 is decommissioned:

before

"name" : "Hadoop:service=StorageContainerManager,name=NodeDecommissionMetrics",
    "modelerType" : "NodeDecommissionMetrics",
    "tag.Hostname" : "0d207b6cbbf1",
    "TrackedDecommissioningMaintenanceNodesTotal" : 0,
    "TrackedRecommissionNodesTotal" : 0,
    "TrackedPipelinesWaitingToCloseTotal" : 0,
    "TrackedContainersUnderReplicatedTotal" : 0,
    "TrackedContainersUnhealthyTotal" : 0,
    "TrackedContainersSufficientlyReplicatedTotal" : 0
  }, {

during
    "name" : "Hadoop:service=StorageContainerManager,name=NodeDecommissionMetrics",
    "modelerType" : "NodeDecommissionMetrics",
    "tag.Hostname" : "0d207b6cbbf1",
    "TrackedDecommissioningMaintenanceNodesTotal" : 1,
    "TrackedRecommissionNodesTotal" : 0,
    "TrackedPipelinesWaitingToCloseTotal" : 2,
    "TrackedContainersUnderReplicatedTotal" : 0,
    "TrackedContainersUnhealthyTotal" : 0,
    "TrackedContainersSufficientlyReplicatedTotal" : 0,
    "TrackedUnhealthyContainers-ozone-datanode-2.ozone_default" : 0,
    "TrackedSufficientlyReplicated-ozone-datanode-2.ozone_default" : 0,
    "TrackedPipelinesWaitingToClose-ozone-datanode-2.ozone_default" : 2,
    "TrackedUnderReplicated-ozone-datanode-2.ozone_default" : 0
  }, {

  }, {
    "name" : "Hadoop:service=StorageContainerManager,name=NodeDecommissionMetrics",
    "modelerType" : "NodeDecommissionMetrics",
    "tag.Hostname" : "0d207b6cbbf1",
    "TrackedDecommissioningMaintenanceNodesTotal" : 1,
    "TrackedRecommissionNodesTotal" : 0,
    "TrackedPipelinesWaitingToCloseTotal" : 0,
    "TrackedContainersUnderReplicatedTotal" : 1,
    "TrackedContainersUnhealthyTotal" : 0,
    "TrackedContainersSufficientlyReplicatedTotal" : 0,
    "TrackedUnhealthyContainers-ozone-datanode-2.ozone_default" : 0,
    "TrackedSufficientlyReplicated-ozone-datanode-2.ozone_default" : 0,
    "TrackedPipelinesWaitingToClose-ozone-datanode-2.ozone_default" : 0,
    "TrackedUnderReplicated-ozone-datanode-2.ozone_default" : 1
  }, {

after
 }, {
    "name" : "Hadoop:service=StorageContainerManager,name=NodeDecommissionMetrics",
    "modelerType" : "NodeDecommissionMetrics",
    "tag.Hostname" : "0d207b6cbbf1",
    "TrackedDecommissioningMaintenanceNodesTotal" : 0,
    "TrackedRecommissionNodesTotal" : 0,
    "TrackedPipelinesWaitingToCloseTotal" : 0,
    "TrackedContainersUnderReplicatedTotal" : 0,
    "TrackedContainersUnhealthyTotal" : 0,
    "TrackedContainersSufficientlyReplicatedTotal" : 0
  }, {

The host datanode-2 metrics no longer visible as the node exits the workflow.

This seems to follow how hadoop handles metrics collected dynamically, however the prom endpoint seems to retain the last pushed value for some reason. Is this what we should expect when collecting metrics for hosts as they go in and out of the workflow?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure how the prom end point works. Its not ideal that it keeps the last value pushed, but I am not sure where that code even comes from!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. We should go forward with using this implementation that works for JMX metrics for completing this PR to expose decommission / maintenance metrics via JMX and open a new jira to look into supporting the prom endpoint. This PR supports metrics tracking the decommission and maintenance workflow both with aggregated counts and DN host specific counts. A jira will be filed to track prom endpoint behavior for the metrics. What do you think?

containerStateByHost.entrySet()) {
metrics.metricRecordOfReplicationByHost(e.getKey(),
e.getValue().sufficientlyReplicated,
e.getValue().underReplicatedContainers,
e.getValue().unhealthyContainers);
}
}

void resetContainerMetrics() {
pipelinesWaitingToClose = 0;
sufficientlyReplicatedContainers = 0;
unhealthyContainers = 0;
underReplicatedContainers = 0;

for (Map.Entry<String, ContainerStateInWorkflow> e :

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

At the moment, to reset things, you need to iterate this map and clear all values. Then iterate it again and remove any entries that are no longer tracked.

What if, you just clear the map, which is a one liner.

Then make ContainerStateInWorkflow a public inner class, and pass the Map<String, ContainerStateInWorkflow> directly to the metrics class and have it replace all its metrics internally with the new map.

That way reset becomes a lot easier too.

Also, if we add another metric for a host, we don't need to add another parameter to the metricRecordOfContainerStateByHost method, as it just receives the wrapper object anyway.

@neils-dev neils-dev Oct 25, 2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, thanks, I was looking to couple the ContainerStateInWorkflow for use both in the DatanodeAdminMonitorImpland in the NodeDecommissionMetrics however there are a few issues with that and thus it is implemented this way. Those issues are,

i.) there needs to be separate stores for numbers collected for the metrics from the monitor and numbers stored in the NodeDecommissionMetrics. This is so that we do not report incomplete intermediate numbers to the NodeDecommissionMetrics when the metrics are periodically pulled through getMetrics(). We flush the numbers on each run of the monitor thread to the NodeDecommissionMetrics once all the numbers have been collected (the calls to metricRecordOfContainterStateByHost). For this reason the Map<string, ContainerStateInWorkflow> cannot be used directly in the NodeDecommisonMetrics.

ii.) With the two separate stores, we need to know which hosts stored are currently in the workflow and which are out of the workflow and stale. Thus the check in the monitor code to collect those hosts that are stale and reporting that to the NodeDecommissionMetrics.metricRemoveRecordOfContainerStateByHost(). For this reason as well, it looks like clearing the map on each run of the monitor instead of iterating to reset to 0, as suggested, is not possible. We need to know which nodes (hosts) have become stale since the last run of the monitor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does the implementation as it stands now, protect against incomplete intermediate metrics? The metrics are snapshot via the call to getMetrics, but the metrics are set over several calls from the Decommission monitor to the metrics class and there is no synchronisation. Could we not set trackedPipelinesWaitingToCloseTotal and then before trackedContainersUnderReplicatedTotal is set, getMetrics is called giving an inconsistent result?

Probably, getMetrics() and any setters need synchronized, and even then you need to set everything in a single synchronized call.

We can build up a Map<String, ContainerStateInWorkflow> for each iteration and at the end of the iteration pass it to the metrics object like new Map<>(mapJustBuiltUp) - then you can clear the original and the Map and ContainerStateInWorkflow objects are referenced only in the new metrics class and will not be changed again.

Replace them all on the next call and then we don't need to worry about expiring individual nodes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

On each scheduled run of the monitor, the implementation captures the current workflow state completely prior to flushing the metric update to the NodeDecommissionMetric object (DatanodeAdminMonitorImpl.setMetricsToGauge). In doing so, it tries to keep each metric refresh pull from getMetrics display a full snapshot of the last captured run of the monitor.

We can build up a Map<String, ContainerStateInWorkflow> for each iteration and at the end of the iteration pass it to the metrics object like new Map<>(mapJustBuiltUp)

Yes, I currently have been coding something just like that based on your earlier comment. With this, the Map<String, ContainerStateInWorkflow> is passed to the NodeDecommisionMetric.metricRecordOfContainerStateByHost. Within it, it sets the internal metrics by host and also removes stale nodes that exited the workflow since the last run of the monitor snapshot captured. It looks like what we are discussing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

On each scheduled run of the monitor, the implementation captures the current workflow state completely prior to flushing the metric update to the NodeDecommissionMetric object (DatanodeAdminMonitorImpl.setMetricsToGauge). In doing so, it tries to keep each metric refresh pull from getMetrics display a full snapshot of the last captured run of the monitor.

The code in DatanodeAdminMonitorImpl.setMetricsToGauge is:

synchronized void setMetricsToGauge() {
    metrics.setTrackedContainersUnhealthyTotal(unhealthyContainers);
    metrics.setTrackedRecommissionNodesTotal(trackedRecommission);
    metrics.setTrackedDecommissioningMaintenanceNodesTotal(
            trackedDecomMaintenance);
    metrics.setTrackedContainersUnderReplicatedTotal(
            underReplicatedContainers);
    metrics.setTrackedContainersSufficientlyReplicatedTotal(
            sufficientlyReplicatedContainers);
    metrics.setTrackedPipelinesWaitingToCloseTotal(pipelinesWaitingToClose);
    for (Map.Entry<String, ContainerStateInWorkflow> e :
...

It makes multiple calls to metrics and there is nothing stopping getMetrics() being called on the metrics object by another thread half way through the execution of setMetricsToGauge. This means the metrics can still be snapshot inconsistently.

To make it consistent, you need to synchronize in the metrics object and set ALL the metrics in a single synchronized call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To make it consistent, you need to synchronize in the metrics object and set ALL the metrics in a single synchronized call.

Will do. At least try to keep possible inconsistency to a minimum. We won't report metrics that show one value in one sample, that clear, then go back to value due to our sampling from the monitor.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is this something we should look to add to the code, adding in a single call to the metrics object for all collected metrics to update? Modify DatanodeAdminMonitorImpl.setMetricsToGauge?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Latest commit contains the modifications we are discussing -

We can build up a Map<String, ContainerStateInWorkflow> for each iteration and at the end of the iteration pass it to the metrics object like new Map<>(mapJustBuiltUp)

Each iteration in the monitor collects snapshot of node metrics in workflow within threadLocal variables.

Then make ContainerStateInWorkflow a public inner class, and pass the Map<String, ContainerStateInWorkflow> directly to the metrics class and have it replace all its metrics internally with the new map.

The NodeDecommissionMetrics uses the ContainerStateInWorkflow for each node to set the metric gauges pulled by getMetrics. Each iteration clears the internal maps.

containerStateByHost.entrySet()) {
e.getValue().reset();
}
pipelinesWaitingToCloseByHost.replaceAll((k, v) -> 0L);
}

private void processCancelledNodes() {
while (!cancelledNodes.isEmpty()) {
DatanodeDetails dn = cancelledNodes.poll();
Expand All @@ -188,7 +283,9 @@ private void processPendingNodes() {
}

private void processTransitioningNodes() {
resetContainerMetrics();
Iterator<DatanodeDetails> iterator = trackedNodes.iterator();

while (iterator.hasNext()) {
DatanodeDetails dn = iterator.next();
try {
Expand Down Expand Up @@ -278,6 +375,9 @@ private boolean checkPipelinesClosedOnNode(DatanodeDetails dn)
} else {
LOG.info("Waiting for pipelines to close for {}. There are {} " +
"pipelines", dn, pipelines.size());
pipelinesWaitingToCloseByHost.put(dn.getHostName(),
(long) pipelines.size());
pipelinesWaitingToClose += pipelines.size();
return false;
}
}
Expand Down Expand Up @@ -327,6 +427,17 @@ private boolean checkContainersReplicatedOnNode(DatanodeDetails dn)
LOG.info("{} has {} sufficientlyReplicated, {} underReplicated and {} " +
"unhealthy containers",
dn, sufficientlyReplicated, underReplicated, unhealthy);
containerStateByHost.computeIfAbsent(dn.getHostName(),
hostID -> new ContainerStateInWorkflow(hostID,
Comment thread
sodonnel marked this conversation as resolved.
Outdated
0L,
0L,
0L)
).setAll(sufficientlyReplicated,
underReplicated,
unhealthy);
sufficientlyReplicatedContainers += sufficientlyReplicated;
underReplicatedContainers += underReplicated;
unhealthyContainers += unhealthy;
if (LOG.isDebugEnabled() && underReplicatedIDs.size() < 10000 &&
unhealthyIDs.size() < 10000) {
LOG.debug("{} has {} underReplicated [{}] and {} unhealthy [{}] " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public class NodeDecommissionManager {
private boolean useHostnames;
private long monitorInterval;

// Decommissioning and Maintenance mode progress related metrics.
private NodeDecommissionMetrics metrics;

private static final Logger LOG =
LoggerFactory.getLogger(NodeDecommissionManager.class);

Expand Down Expand Up @@ -181,6 +184,7 @@ public NodeDecommissionManager(OzoneConfiguration config, NodeManager nm,
this.scmContext = scmContext;
this.eventQueue = eventQueue;
this.replicationManager = rm;
this.metrics = null;

executor = Executors.newScheduledThreadPool(1,
new ThreadFactoryBuilder().setNameFormat("DatanodeAdminManager-%d")
Expand Down Expand Up @@ -208,7 +212,8 @@ public NodeDecommissionManager(OzoneConfiguration config, NodeManager nm,

monitor = new DatanodeAdminMonitorImpl(conf, eventQueue, nodeManager,
replicationManager);

this.metrics = NodeDecommissionMetrics.create();
monitor.setMetrics(this.metrics);
executor.scheduleAtFixedRate(monitor, monitorInterval, monitorInterval,
TimeUnit.SECONDS);
}
Expand Down Expand Up @@ -373,6 +378,7 @@ public synchronized void startMaintenance(DatanodeDetails dn, int endInHours)
* Stops the decommission monitor from running when SCM is shutdown.
*/
public void stop() {
metrics.unRegister();
if (executor != null) {
executor.shutdown();
}
Expand Down
Loading