|
@@ -14,7 +14,6 @@ import org.elasticsearch.cluster.LocalNodeMasterListener;
|
|
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
|
|
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
|
|
|
import org.elasticsearch.cluster.service.ClusterService;
|
|
|
-import org.elasticsearch.common.component.LifecycleListener;
|
|
|
import org.elasticsearch.common.settings.Setting;
|
|
|
import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
|
@@ -29,12 +28,16 @@ import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderContext;
|
|
|
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderResult;
|
|
|
import org.elasticsearch.xpack.autoscaling.capacity.AutoscalingDeciderService;
|
|
|
import org.elasticsearch.xpack.core.ml.MlTasks;
|
|
|
+import org.elasticsearch.xpack.core.ml.action.OpenJobAction;
|
|
|
+import org.elasticsearch.xpack.core.ml.action.StartDataFrameAnalyticsAction;
|
|
|
import org.elasticsearch.xpack.core.ml.action.StartDatafeedAction.DatafeedParams;
|
|
|
import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsState;
|
|
|
import org.elasticsearch.xpack.core.ml.inference.allocation.AllocationState;
|
|
|
import org.elasticsearch.xpack.core.ml.inference.allocation.TrainedModelAllocation;
|
|
|
import org.elasticsearch.xpack.core.ml.job.config.AnalysisLimits;
|
|
|
import org.elasticsearch.xpack.core.ml.job.config.JobState;
|
|
|
+import org.elasticsearch.xpack.core.ml.job.snapshot.upgrade.SnapshotUpgradeState;
|
|
|
+import org.elasticsearch.xpack.core.ml.job.snapshot.upgrade.SnapshotUpgradeTaskParams;
|
|
|
import org.elasticsearch.xpack.ml.MachineLearning;
|
|
|
import org.elasticsearch.xpack.ml.inference.allocation.TrainedModelAllocationMetadata;
|
|
|
import org.elasticsearch.xpack.ml.job.NodeLoad;
|
|
@@ -65,6 +68,7 @@ import java.util.stream.Stream;
|
|
|
|
|
|
import static org.elasticsearch.xpack.core.ml.MlTasks.getDataFrameAnalyticsState;
|
|
|
import static org.elasticsearch.xpack.core.ml.MlTasks.getJobStateModifiedForReassignments;
|
|
|
+import static org.elasticsearch.xpack.core.ml.MlTasks.getSnapshotUpgradeState;
|
|
|
import static org.elasticsearch.xpack.ml.MachineLearning.MAX_OPEN_JOBS_PER_NODE;
|
|
|
import static org.elasticsearch.xpack.ml.MachineLearning.NATIVE_EXECUTABLE_CODE_OVERHEAD;
|
|
|
import static org.elasticsearch.xpack.ml.job.JobNodeSelector.AWAITING_LAZY_ASSIGNMENT;
|
|
@@ -88,7 +92,6 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
private final LongSupplier timeSupplier;
|
|
|
|
|
|
private volatile boolean isMaster;
|
|
|
- private volatile boolean running;
|
|
|
private volatile int maxMachineMemoryPercent;
|
|
|
private volatile int maxOpenJobs;
|
|
|
private volatile boolean useAuto;
|
|
@@ -117,17 +120,6 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
clusterService.getClusterSettings().addSettingsUpdateConsumer(MAX_OPEN_JOBS_PER_NODE, this::setMaxOpenJobs);
|
|
|
clusterService.getClusterSettings().addSettingsUpdateConsumer(MachineLearning.USE_AUTO_MACHINE_MEMORY_PERCENT, this::setUseAuto);
|
|
|
clusterService.addLocalNodeMasterListener(this);
|
|
|
- clusterService.addLifecycleListener(new LifecycleListener() {
|
|
|
- @Override
|
|
|
- public void afterStart() {
|
|
|
- running = true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void beforeStop() {
|
|
|
- running = false;
|
|
|
- }
|
|
|
- });
|
|
|
}
|
|
|
|
|
|
static OptionalLong getNodeJvmSize(DiscoveryNode node) {
|
|
@@ -197,7 +189,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
}
|
|
|
PriorityQueue<NodeLoad.Builder> mostFreeMemoryFirst = new PriorityQueue<>(
|
|
|
nodeLoads.size(),
|
|
|
- // If we have no more remaining jobs, its the same as having no more free memory
|
|
|
+ // If we have no more remaining jobs, it's the same as having no more free memory
|
|
|
Comparator.<NodeLoad.Builder>comparingLong(v -> v.remainingJobs() == 0 ? 0L : v.getFreeMemory()).reversed()
|
|
|
);
|
|
|
for (NodeLoad load : nodeLoads) {
|
|
@@ -258,6 +250,14 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
return tasksCustomMetadata.findTasks(MlTasks.JOB_TASK_NAME, t -> taskStateFilter(getJobStateModifiedForReassignments(t)));
|
|
|
}
|
|
|
|
|
|
+ private static Collection<PersistentTask<?>> snapshotUpgradeTasks(PersistentTasksCustomMetadata tasksCustomMetadata) {
|
|
|
+ if (tasksCustomMetadata == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ return tasksCustomMetadata.findTasks(MlTasks.JOB_SNAPSHOT_UPGRADE_TASK_NAME, t -> taskStateFilter(getSnapshotUpgradeState(t)));
|
|
|
+ }
|
|
|
+
|
|
|
private static Collection<PersistentTask<?>> dataframeAnalyticsTasks(PersistentTasksCustomMetadata tasksCustomMetadata) {
|
|
|
if (tasksCustomMetadata == null) {
|
|
|
return Collections.emptyList();
|
|
@@ -352,15 +352,20 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
|
|
|
PersistentTasksCustomMetadata tasks = clusterState.getMetadata().custom(PersistentTasksCustomMetadata.TYPE);
|
|
|
Collection<PersistentTask<?>> anomalyDetectionTasks = anomalyDetectionTasks(tasks);
|
|
|
+ Collection<PersistentTask<?>> snapshotUpgradeTasks = snapshotUpgradeTasks(tasks);
|
|
|
Collection<PersistentTask<?>> dataframeAnalyticsTasks = dataframeAnalyticsTasks(tasks);
|
|
|
Map<String, TrainedModelAllocation> modelAllocations = TrainedModelAllocationMetadata.fromState(clusterState).modelAllocations();
|
|
|
final List<String> waitingAnomalyJobs = anomalyDetectionTasks.stream()
|
|
|
.filter(t -> AWAITING_LAZY_ASSIGNMENT.equals(t.getAssignment()))
|
|
|
- .map(t -> MlTasks.jobId(t.getId()))
|
|
|
+ .map(t -> ((OpenJobAction.JobParams) t.getParams()).getJobId())
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ final List<String> waitingSnapshotUpgrades = snapshotUpgradeTasks.stream()
|
|
|
+ .filter(t -> AWAITING_LAZY_ASSIGNMENT.equals(t.getAssignment()))
|
|
|
+ .map(t -> ((SnapshotUpgradeTaskParams) t.getParams()).getJobId())
|
|
|
.collect(Collectors.toList());
|
|
|
final List<String> waitingAnalyticsJobs = dataframeAnalyticsTasks.stream()
|
|
|
.filter(t -> AWAITING_LAZY_ASSIGNMENT.equals(t.getAssignment()))
|
|
|
- .map(t -> MlTasks.dataFrameAnalyticsId(t.getId()))
|
|
|
+ .map(t -> ((StartDataFrameAnalyticsAction.TaskParams) t.getParams()).getId())
|
|
|
.collect(Collectors.toList());
|
|
|
final List<String> waitingAllocatedModels = modelAllocations.entrySet()
|
|
|
.stream()
|
|
@@ -377,6 +382,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
|
|
|
final MlScalingReason.Builder reasonBuilder = MlScalingReason.builder()
|
|
|
.setWaitingAnomalyJobs(waitingAnomalyJobs)
|
|
|
+ .setWaitingSnapshotUpgrades(waitingSnapshotUpgrades)
|
|
|
.setWaitingAnalyticsJobs(waitingAnalyticsJobs)
|
|
|
.setWaitingModels(waitingAllocatedModels)
|
|
|
.setCurrentMlCapacity(currentScale.autoscalingCapacity(maxMachineMemoryPercent, useAuto))
|
|
@@ -385,9 +391,16 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
// There are no ML nodes, scale up as quick as possible, no matter if memory is stale or not
|
|
|
if (nodes.isEmpty()
|
|
|
&& (waitingAnomalyJobs.isEmpty() == false
|
|
|
+ || waitingSnapshotUpgrades.isEmpty() == false
|
|
|
|| waitingAnalyticsJobs.isEmpty() == false
|
|
|
|| waitingAllocatedModels.isEmpty() == false)) {
|
|
|
- return scaleUpFromZero(waitingAnomalyJobs, waitingAnalyticsJobs, waitingAllocatedModels, reasonBuilder);
|
|
|
+ return scaleUpFromZero(
|
|
|
+ waitingAnomalyJobs,
|
|
|
+ waitingSnapshotUpgrades,
|
|
|
+ waitingAnalyticsJobs,
|
|
|
+ waitingAllocatedModels,
|
|
|
+ reasonBuilder
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
// We don't need to check anything as there are no tasks
|
|
@@ -477,6 +490,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
numAnalyticsJobsInQueue,
|
|
|
nodeLoads,
|
|
|
waitingAnomalyJobs,
|
|
|
+ waitingSnapshotUpgrades,
|
|
|
waitingAnalyticsJobs,
|
|
|
waitingAllocatedModels,
|
|
|
futureFreedCapacity.orElse(null),
|
|
@@ -488,7 +502,9 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
resetScaleDownCoolDown();
|
|
|
return scaleUpDecision.get();
|
|
|
}
|
|
|
- if (waitingAnalyticsJobs.isEmpty() == false || waitingAnomalyJobs.isEmpty() == false) {
|
|
|
+ if (waitingAnalyticsJobs.isEmpty() == false
|
|
|
+ || waitingSnapshotUpgrades.isEmpty() == false
|
|
|
+ || waitingAnomalyJobs.isEmpty() == false) {
|
|
|
// We don't want to continue to consider a scale down if there are now waiting jobs
|
|
|
resetScaleDownCoolDown();
|
|
|
return noScaleResultOrRefresh(
|
|
@@ -499,9 +515,11 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
reasonBuilder.setSimpleReason(
|
|
|
String.format(
|
|
|
Locale.ROOT,
|
|
|
- "Passing currently perceived capacity as there are [%d] analytics and [%d] anomaly jobs in the queue, "
|
|
|
+ "Passing currently perceived capacity as there are [%d] model snapshot upgrades, "
|
|
|
+ + "[%d] analytics and [%d] anomaly detection jobs in the queue, "
|
|
|
+ "but the number in the queue is less than the configured maximum allowed "
|
|
|
+ " or the queued jobs will eventually be assignable at the current size. ",
|
|
|
+ waitingSnapshotUpgrades.size(),
|
|
|
waitingAnalyticsJobs.size(),
|
|
|
waitingAnomalyJobs.size()
|
|
|
)
|
|
@@ -690,6 +708,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
// can eventually start, and given the current cluster, no job can eventually start.
|
|
|
AutoscalingDeciderResult scaleUpFromZero(
|
|
|
List<String> waitingAnomalyJobs,
|
|
|
+ List<String> waitingSnapshotUpgrades,
|
|
|
List<String> waitingAnalyticsJobs,
|
|
|
List<String> waitingAllocatedModels,
|
|
|
MlScalingReason.Builder reasonBuilder
|
|
@@ -704,17 +723,23 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
this::getAnomalyMemoryRequirement,
|
|
|
0
|
|
|
);
|
|
|
+ final Optional<NativeMemoryCapacity> snapshotUpgradeCapacity = requiredCapacityForUnassignedJobs(
|
|
|
+ waitingSnapshotUpgrades,
|
|
|
+ this::getAnomalyMemoryRequirement,
|
|
|
+ 0
|
|
|
+ );
|
|
|
final Optional<NativeMemoryCapacity> allocatedModelCapacity = requiredCapacityForUnassignedJobs(
|
|
|
waitingAllocatedModels,
|
|
|
this::getAllocatedModelRequirement,
|
|
|
0
|
|
|
);
|
|
|
NativeMemoryCapacity updatedCapacity = NativeMemoryCapacity.ZERO.merge(anomalyCapacity.orElse(NativeMemoryCapacity.ZERO))
|
|
|
+ .merge(snapshotUpgradeCapacity.orElse(NativeMemoryCapacity.ZERO))
|
|
|
.merge(analyticsCapacity.orElse(NativeMemoryCapacity.ZERO))
|
|
|
.merge(allocatedModelCapacity.orElse(NativeMemoryCapacity.ZERO));
|
|
|
// If we still have calculated zero, this means the ml memory tracker does not have the required info.
|
|
|
// So, request a scale for the default. This is only for the 0 -> N scaling case.
|
|
|
- if (updatedCapacity.getNode() == 0L) {
|
|
|
+ if (updatedCapacity.getNodeMlNativeMemoryRequirement() == 0L) {
|
|
|
updatedCapacity.merge(
|
|
|
new NativeMemoryCapacity(
|
|
|
ByteSizeValue.ofMb(AnalysisLimits.DEFAULT_MODEL_MEMORY_LIMIT_MB).getBytes(),
|
|
@@ -744,6 +769,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
int numAnalyticsJobsInQueue,
|
|
|
List<NodeLoad> nodeLoads,
|
|
|
List<String> waitingAnomalyJobs,
|
|
|
+ List<String> waitingSnapshotUpgrades,
|
|
|
List<String> waitingAnalyticsJobs,
|
|
|
List<String> waitingAllocatedModels,
|
|
|
@Nullable NativeMemoryCapacity futureFreedCapacity,
|
|
@@ -753,11 +779,11 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
|
|
|
// Are we in breach of maximum waiting jobs?
|
|
|
if (waitingAnalyticsJobs.size() > numAnalyticsJobsInQueue
|
|
|
- || waitingAnomalyJobs.size() > numAnomalyJobsInQueue
|
|
|
+ || waitingAnomalyJobs.size() + waitingSnapshotUpgrades.size() > numAnomalyJobsInQueue
|
|
|
|| waitingAllocatedModels.size() > 0) {
|
|
|
|
|
|
Tuple<NativeMemoryCapacity, List<NodeLoad>> anomalyCapacityAndNewLoad = determineUnassignableJobs(
|
|
|
- waitingAnomalyJobs,
|
|
|
+ Stream.concat(waitingAnomalyJobs.stream(), waitingSnapshotUpgrades.stream()).collect(Collectors.toList()),
|
|
|
this::getAnomalyMemoryRequirement,
|
|
|
numAnomalyJobsInQueue,
|
|
|
nodeLoads
|
|
@@ -808,16 +834,21 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
|
|
|
// Could the currently waiting jobs ever be assigned?
|
|
|
// NOTE: the previous predicate catches if an allocated model isn't assigned
|
|
|
- if (waitingAnalyticsJobs.isEmpty() == false || waitingAnomalyJobs.isEmpty() == false) {
|
|
|
+ if (waitingAnalyticsJobs.isEmpty() == false
|
|
|
+ || waitingSnapshotUpgrades.isEmpty() == false
|
|
|
+ || waitingAnomalyJobs.isEmpty() == false) {
|
|
|
// we are unable to determine new tier size, but maybe we can see if our nodes are big enough.
|
|
|
if (futureFreedCapacity == null) {
|
|
|
Optional<Long> maxSize = Stream.concat(
|
|
|
waitingAnalyticsJobs.stream().map(mlMemoryTracker::getDataFrameAnalyticsJobMemoryRequirement),
|
|
|
- waitingAnomalyJobs.stream().map(mlMemoryTracker::getAnomalyDetectorJobMemoryRequirement)
|
|
|
+ Stream.concat(
|
|
|
+ waitingAnomalyJobs.stream().map(mlMemoryTracker::getAnomalyDetectorJobMemoryRequirement),
|
|
|
+ waitingSnapshotUpgrades.stream().map(mlMemoryTracker::getAnomalyDetectorJobMemoryRequirement)
|
|
|
+ )
|
|
|
).filter(Objects::nonNull).max(Long::compareTo);
|
|
|
- if (maxSize.isPresent() && maxSize.get() > currentScale.getNode()) {
|
|
|
+ if (maxSize.isPresent() && maxSize.get() > currentScale.getNodeMlNativeMemoryRequirement()) {
|
|
|
AutoscalingCapacity requiredCapacity = new NativeMemoryCapacity(
|
|
|
- Math.max(currentScale.getTier(), maxSize.get()),
|
|
|
+ Math.max(currentScale.getTierMlNativeMemoryRequirement(), maxSize.get()),
|
|
|
maxSize.get()
|
|
|
).autoscalingCapacity(maxMachineMemoryPercent, useAuto);
|
|
|
return Optional.of(
|
|
@@ -834,7 +865,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
}
|
|
|
long newTierNeeded = 0L;
|
|
|
// could any of the nodes actually run the job?
|
|
|
- long newNodeMax = currentScale.getNode();
|
|
|
+ long newNodeMax = currentScale.getNodeMlNativeMemoryRequirement();
|
|
|
for (String analyticsJob : waitingAnalyticsJobs) {
|
|
|
Long requiredMemory = mlMemoryTracker.getDataFrameAnalyticsJobMemoryRequirement(analyticsJob);
|
|
|
// it is OK to continue here as we have not breached our queuing limit
|
|
@@ -842,7 +873,7 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
continue;
|
|
|
}
|
|
|
// Is there "future capacity" on a node that could run this job? If not, we need that much more in the tier.
|
|
|
- if (futureFreedCapacity.getNode() < requiredMemory) {
|
|
|
+ if (futureFreedCapacity.getNodeMlNativeMemoryRequirement() < requiredMemory) {
|
|
|
newTierNeeded = Math.max(requiredMemory, newTierNeeded);
|
|
|
}
|
|
|
newNodeMax = Math.max(newNodeMax, requiredMemory);
|
|
@@ -854,12 +885,24 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
continue;
|
|
|
}
|
|
|
// Is there "future capacity" on a node that could run this job? If not, we need that much more in the tier.
|
|
|
- if (futureFreedCapacity.getNode() < requiredMemory) {
|
|
|
+ if (futureFreedCapacity.getNodeMlNativeMemoryRequirement() < requiredMemory) {
|
|
|
newTierNeeded = Math.max(requiredMemory, newTierNeeded);
|
|
|
}
|
|
|
newNodeMax = Math.max(newNodeMax, requiredMemory);
|
|
|
}
|
|
|
- if (newNodeMax > currentScale.getNode() || newTierNeeded > 0L) {
|
|
|
+ for (String snapshotUpgrade : waitingSnapshotUpgrades) {
|
|
|
+ Long requiredMemory = mlMemoryTracker.getAnomalyDetectorJobMemoryRequirement(snapshotUpgrade);
|
|
|
+ // it is OK to continue here as we have not breached our queuing limit
|
|
|
+ if (requiredMemory == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // Is there "future capacity" on a node that could run this job? If not, we need that much more in the tier.
|
|
|
+ if (futureFreedCapacity.getNodeMlNativeMemoryRequirement() < requiredMemory) {
|
|
|
+ newTierNeeded = Math.max(requiredMemory, newTierNeeded);
|
|
|
+ }
|
|
|
+ newNodeMax = Math.max(newNodeMax, requiredMemory);
|
|
|
+ }
|
|
|
+ if (newNodeMax > currentScale.getNodeMlNativeMemoryRequirement() || newTierNeeded > 0L) {
|
|
|
NativeMemoryCapacity newCapacity = new NativeMemoryCapacity(newTierNeeded, newNodeMax);
|
|
|
AutoscalingCapacity requiredCapacity = NativeMemoryCapacity.from(currentScale)
|
|
|
.merge(newCapacity)
|
|
@@ -983,15 +1026,16 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
}
|
|
|
// We consider a scale down if we are not fully utilizing the tier
|
|
|
// Or our largest job could be on a smaller node (meaning the same size tier but smaller nodes are possible).
|
|
|
- if (currentlyNecessaryTier < currentCapacity.getTier() || currentlyNecessaryNode < currentCapacity.getNode()) {
|
|
|
+ if (currentlyNecessaryTier < currentCapacity.getTierMlNativeMemoryRequirement()
|
|
|
+ || currentlyNecessaryNode < currentCapacity.getNodeMlNativeMemoryRequirement()) {
|
|
|
NativeMemoryCapacity nativeMemoryCapacity = new NativeMemoryCapacity(
|
|
|
// Since we are in the `scaleDown` branch, we know jobs are running and we could be smaller
|
|
|
// If we have some weird rounding errors, it may be that the `currentlyNecessary` values are larger than
|
|
|
// current capacity. We never want to accidentally say "scale up" via a scale down.
|
|
|
- Math.min(currentlyNecessaryTier, currentCapacity.getTier()),
|
|
|
- Math.min(currentlyNecessaryNode, currentCapacity.getNode()),
|
|
|
+ Math.min(currentlyNecessaryTier, currentCapacity.getTierMlNativeMemoryRequirement()),
|
|
|
+ Math.min(currentlyNecessaryNode, currentCapacity.getNodeMlNativeMemoryRequirement()),
|
|
|
// If our newly suggested native capacity is the same, we can use the previously stored jvm size
|
|
|
- currentlyNecessaryNode == currentCapacity.getNode() ? currentCapacity.getJvmSize() : null
|
|
|
+ currentlyNecessaryNode == currentCapacity.getNodeMlNativeMemoryRequirement() ? currentCapacity.getJvmSize() : null
|
|
|
);
|
|
|
AutoscalingCapacity requiredCapacity = nativeMemoryCapacity.autoscalingCapacity(maxMachineMemoryPercent, useAuto);
|
|
|
return Optional.of(
|
|
@@ -1035,6 +1079,10 @@ public class MlAutoscalingDeciderService implements AutoscalingDeciderService, L
|
|
|
return jobState == null || jobState.isNoneOf(JobState.CLOSED, JobState.FAILED);
|
|
|
}
|
|
|
|
|
|
+ private static boolean taskStateFilter(SnapshotUpgradeState snapshotUpgradeState) {
|
|
|
+ return snapshotUpgradeState == null || snapshotUpgradeState.isNoneOf(SnapshotUpgradeState.STOPPED, SnapshotUpgradeState.FAILED);
|
|
|
+ }
|
|
|
+
|
|
|
private static boolean taskStateFilter(DataFrameAnalyticsState dataFrameAnalyticsState) {
|
|
|
// Don't count stopped and failed df-analytics tasks as they don't consume native memory
|
|
|
return dataFrameAnalyticsState == null
|