Merge branch 'master' into feature/sql
Original commit: elastic/x-pack-elasticsearch@d11ddc7a2c
This commit is contained in:
commit
61f13b9642
|
@ -40,6 +40,13 @@ default behavior.
|
||||||
`xpack.ml.max_open_jobs`::
|
`xpack.ml.max_open_jobs`::
|
||||||
The maximum number of jobs that can run on a node. Defaults to `10`.
|
The maximum number of jobs that can run on a node. Defaults to `10`.
|
||||||
|
|
||||||
|
`xpack.ml.max_machine_memory_percent`::
|
||||||
|
The maximum percentage of the machine's memory that {ml} may use for running
|
||||||
|
analytics processes. (These processes are separate to the {es} JVM.) Defaults to
|
||||||
|
`30` percent. The limit is based on the total memory of the machine, not current
|
||||||
|
free memory. Jobs will not be allocated to a node if doing so would cause the
|
||||||
|
estimated memory use of {ml} jobs to exceed the limit.
|
||||||
|
|
||||||
`xpack.ml.max_model_memory_limit`::
|
`xpack.ml.max_model_memory_limit`::
|
||||||
The maximum `model_memory_limit` property value that can be set for any job on
|
The maximum `model_memory_limit` property value that can be set for any job on
|
||||||
this node. If you try to create a job with a `model_memory_limit` property value
|
this node. If you try to create a job with a `model_memory_limit` property value
|
||||||
|
|
|
@ -23,11 +23,14 @@ import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Setting.Property;
|
import org.elasticsearch.common.settings.Setting.Property;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.settings.SettingsFilter;
|
import org.elasticsearch.common.settings.SettingsFilter;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
|
import org.elasticsearch.monitor.os.OsProbe;
|
||||||
|
import org.elasticsearch.monitor.os.OsStats;
|
||||||
import org.elasticsearch.plugins.ActionPlugin;
|
import org.elasticsearch.plugins.ActionPlugin;
|
||||||
import org.elasticsearch.rest.RestController;
|
import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestHandler;
|
import org.elasticsearch.rest.RestHandler;
|
||||||
|
@ -138,6 +141,7 @@ import org.elasticsearch.xpack.persistent.PersistentTasksService;
|
||||||
import org.elasticsearch.xpack.security.InternalClient;
|
import org.elasticsearch.xpack.security.InternalClient;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -161,10 +165,13 @@ public class MachineLearning implements ActionPlugin {
|
||||||
Setting.boolSetting("node.ml", XPackSettings.MACHINE_LEARNING_ENABLED, Property.NodeScope);
|
Setting.boolSetting("node.ml", XPackSettings.MACHINE_LEARNING_ENABLED, Property.NodeScope);
|
||||||
public static final String ML_ENABLED_NODE_ATTR = "ml.enabled";
|
public static final String ML_ENABLED_NODE_ATTR = "ml.enabled";
|
||||||
public static final String MAX_OPEN_JOBS_NODE_ATTR = "ml.max_open_jobs";
|
public static final String MAX_OPEN_JOBS_NODE_ATTR = "ml.max_open_jobs";
|
||||||
|
public static final String MACHINE_MEMORY_NODE_ATTR = "ml.machine_memory";
|
||||||
public static final Setting<Integer> CONCURRENT_JOB_ALLOCATIONS =
|
public static final Setting<Integer> CONCURRENT_JOB_ALLOCATIONS =
|
||||||
Setting.intSetting("xpack.ml.node_concurrent_job_allocations", 2, 0, Property.Dynamic, Property.NodeScope);
|
Setting.intSetting("xpack.ml.node_concurrent_job_allocations", 2, 0, Property.Dynamic, Property.NodeScope);
|
||||||
public static final Setting<ByteSizeValue> MAX_MODEL_MEMORY_LIMIT =
|
public static final Setting<ByteSizeValue> MAX_MODEL_MEMORY_LIMIT =
|
||||||
Setting.memorySizeSetting("xpack.ml.max_model_memory_limit", new ByteSizeValue(0), Property.Dynamic, Property.NodeScope);
|
Setting.memorySizeSetting("xpack.ml.max_model_memory_limit", new ByteSizeValue(0), Property.Dynamic, Property.NodeScope);
|
||||||
|
public static final Setting<Integer> MAX_MACHINE_MEMORY_PERCENT =
|
||||||
|
Setting.intSetting("xpack.ml.max_machine_memory_percent", 30, 5, 90, Property.Dynamic, Property.NodeScope);
|
||||||
|
|
||||||
public static final TimeValue STATE_PERSIST_RESTORE_TIMEOUT = TimeValue.timeValueMinutes(30);
|
public static final TimeValue STATE_PERSIST_RESTORE_TIMEOUT = TimeValue.timeValueMinutes(30);
|
||||||
|
|
||||||
|
@ -195,6 +202,7 @@ public class MachineLearning implements ActionPlugin {
|
||||||
ML_ENABLED,
|
ML_ENABLED,
|
||||||
CONCURRENT_JOB_ALLOCATIONS,
|
CONCURRENT_JOB_ALLOCATIONS,
|
||||||
MAX_MODEL_MEMORY_LIMIT,
|
MAX_MODEL_MEMORY_LIMIT,
|
||||||
|
MAX_MACHINE_MEMORY_PERCENT,
|
||||||
ProcessCtrl.DONT_PERSIST_MODEL_STATE_SETTING,
|
ProcessCtrl.DONT_PERSIST_MODEL_STATE_SETTING,
|
||||||
ProcessCtrl.MAX_ANOMALY_RECORDS_SETTING,
|
ProcessCtrl.MAX_ANOMALY_RECORDS_SETTING,
|
||||||
DataCountsReporter.ACCEPTABLE_PERCENTAGE_DATE_PARSE_ERRORS_SETTING,
|
DataCountsReporter.ACCEPTABLE_PERCENTAGE_DATE_PARSE_ERRORS_SETTING,
|
||||||
|
@ -206,9 +214,10 @@ public class MachineLearning implements ActionPlugin {
|
||||||
public Settings additionalSettings() {
|
public Settings additionalSettings() {
|
||||||
String mlEnabledNodeAttrName = "node.attr." + ML_ENABLED_NODE_ATTR;
|
String mlEnabledNodeAttrName = "node.attr." + ML_ENABLED_NODE_ATTR;
|
||||||
String maxOpenJobsPerNodeNodeAttrName = "node.attr." + MAX_OPEN_JOBS_NODE_ATTR;
|
String maxOpenJobsPerNodeNodeAttrName = "node.attr." + MAX_OPEN_JOBS_NODE_ATTR;
|
||||||
|
String machineMemoryAttrName = "node.attr." + MACHINE_MEMORY_NODE_ATTR;
|
||||||
|
|
||||||
if (enabled == false || transportClientMode || tribeNode || tribeNodeClient) {
|
if (enabled == false || transportClientMode || tribeNode || tribeNodeClient) {
|
||||||
disallowMlNodeAttributes(mlEnabledNodeAttrName, maxOpenJobsPerNodeNodeAttrName);
|
disallowMlNodeAttributes(mlEnabledNodeAttrName, maxOpenJobsPerNodeNodeAttrName, machineMemoryAttrName);
|
||||||
return Settings.EMPTY;
|
return Settings.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,8 +228,10 @@ public class MachineLearning implements ActionPlugin {
|
||||||
addMlNodeAttribute(additionalSettings, mlEnabledNodeAttrName, "true");
|
addMlNodeAttribute(additionalSettings, mlEnabledNodeAttrName, "true");
|
||||||
addMlNodeAttribute(additionalSettings, maxOpenJobsPerNodeNodeAttrName,
|
addMlNodeAttribute(additionalSettings, maxOpenJobsPerNodeNodeAttrName,
|
||||||
String.valueOf(AutodetectProcessManager.MAX_OPEN_JOBS_PER_NODE.get(settings)));
|
String.valueOf(AutodetectProcessManager.MAX_OPEN_JOBS_PER_NODE.get(settings)));
|
||||||
|
addMlNodeAttribute(additionalSettings, machineMemoryAttrName,
|
||||||
|
Long.toString(machineMemoryFromStats(OsProbe.getInstance().osStats())));
|
||||||
} else {
|
} else {
|
||||||
disallowMlNodeAttributes(mlEnabledNodeAttrName, maxOpenJobsPerNodeNodeAttrName);
|
disallowMlNodeAttributes(mlEnabledNodeAttrName, maxOpenJobsPerNodeNodeAttrName, machineMemoryAttrName);
|
||||||
}
|
}
|
||||||
return additionalSettings.build();
|
return additionalSettings.build();
|
||||||
}
|
}
|
||||||
|
@ -504,4 +515,25 @@ public class MachineLearning implements ActionPlugin {
|
||||||
maxNumberOfJobs, 200, "xpack.ml.datafeed_thread_pool");
|
maxNumberOfJobs, 200, "xpack.ml.datafeed_thread_pool");
|
||||||
return Arrays.asList(autoDetect, renormalizer, datafeed);
|
return Arrays.asList(autoDetect, renormalizer, datafeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the memory size (in bytes) of the machine this node is running on.
|
||||||
|
* Takes container limits (as used by Docker for example) into account.
|
||||||
|
*/
|
||||||
|
static long machineMemoryFromStats(OsStats stats) {
|
||||||
|
long mem = stats.getMem().getTotal().getBytes();
|
||||||
|
OsStats.Cgroup cgroup = stats.getCgroup();
|
||||||
|
if (cgroup != null) {
|
||||||
|
String containerLimitStr = cgroup.getMemoryLimitInBytes();
|
||||||
|
if (containerLimitStr != null) {
|
||||||
|
BigInteger containerLimit = new BigInteger(containerLimitStr);
|
||||||
|
if ((containerLimit.compareTo(BigInteger.valueOf(mem)) < 0 && containerLimit.compareTo(BigInteger.ZERO) > 0)
|
||||||
|
// mem < 0 means the value couldn't be obtained for some reason
|
||||||
|
|| (mem < 0 && containerLimit.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) < 0)) {
|
||||||
|
mem = containerLimit.longValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ import org.elasticsearch.xpack.security.InternalClient;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -573,6 +574,7 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
*/
|
*/
|
||||||
private final int fallbackMaxNumberOfOpenJobs;
|
private final int fallbackMaxNumberOfOpenJobs;
|
||||||
private volatile int maxConcurrentJobAllocations;
|
private volatile int maxConcurrentJobAllocations;
|
||||||
|
private volatile int maxMachineMemoryPercent;
|
||||||
|
|
||||||
public OpenJobPersistentTasksExecutor(Settings settings, ClusterService clusterService,
|
public OpenJobPersistentTasksExecutor(Settings settings, ClusterService clusterService,
|
||||||
AutodetectProcessManager autodetectProcessManager) {
|
AutodetectProcessManager autodetectProcessManager) {
|
||||||
|
@ -580,14 +582,17 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
this.autodetectProcessManager = autodetectProcessManager;
|
this.autodetectProcessManager = autodetectProcessManager;
|
||||||
this.fallbackMaxNumberOfOpenJobs = AutodetectProcessManager.MAX_OPEN_JOBS_PER_NODE.get(settings);
|
this.fallbackMaxNumberOfOpenJobs = AutodetectProcessManager.MAX_OPEN_JOBS_PER_NODE.get(settings);
|
||||||
this.maxConcurrentJobAllocations = MachineLearning.CONCURRENT_JOB_ALLOCATIONS.get(settings);
|
this.maxConcurrentJobAllocations = MachineLearning.CONCURRENT_JOB_ALLOCATIONS.get(settings);
|
||||||
|
this.maxMachineMemoryPercent = MachineLearning.MAX_MACHINE_MEMORY_PERCENT.get(settings);
|
||||||
clusterService.getClusterSettings()
|
clusterService.getClusterSettings()
|
||||||
.addSettingsUpdateConsumer(MachineLearning.CONCURRENT_JOB_ALLOCATIONS, this::setMaxConcurrentJobAllocations);
|
.addSettingsUpdateConsumer(MachineLearning.CONCURRENT_JOB_ALLOCATIONS, this::setMaxConcurrentJobAllocations);
|
||||||
|
clusterService.getClusterSettings()
|
||||||
|
.addSettingsUpdateConsumer(MachineLearning.MAX_MACHINE_MEMORY_PERCENT, this::setMaxMachineMemoryPercent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Assignment getAssignment(JobParams params, ClusterState clusterState) {
|
public Assignment getAssignment(JobParams params, ClusterState clusterState) {
|
||||||
return selectLeastLoadedMlNode(params.getJobId(), clusterState, maxConcurrentJobAllocations, fallbackMaxNumberOfOpenJobs,
|
return selectLeastLoadedMlNode(params.getJobId(), clusterState, maxConcurrentJobAllocations, fallbackMaxNumberOfOpenJobs,
|
||||||
logger);
|
maxMachineMemoryPercent, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -597,7 +602,7 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
MlMetadata mlMetadata = clusterState.metaData().custom(MlMetadata.TYPE);
|
MlMetadata mlMetadata = clusterState.metaData().custom(MlMetadata.TYPE);
|
||||||
OpenJobAction.validate(params.getJobId(), mlMetadata);
|
OpenJobAction.validate(params.getJobId(), mlMetadata);
|
||||||
Assignment assignment = selectLeastLoadedMlNode(params.getJobId(), clusterState, maxConcurrentJobAllocations,
|
Assignment assignment = selectLeastLoadedMlNode(params.getJobId(), clusterState, maxConcurrentJobAllocations,
|
||||||
fallbackMaxNumberOfOpenJobs, logger);
|
fallbackMaxNumberOfOpenJobs, maxMachineMemoryPercent, logger);
|
||||||
if (assignment.getExecutorNode() == null) {
|
if (assignment.getExecutorNode() == null) {
|
||||||
String msg = "Could not open job because no suitable nodes were found, allocation explanation ["
|
String msg = "Could not open job because no suitable nodes were found, allocation explanation ["
|
||||||
+ assignment.getExplanation() + "]";
|
+ assignment.getExplanation() + "]";
|
||||||
|
@ -630,6 +635,12 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
this.maxConcurrentJobAllocations, maxConcurrentJobAllocations);
|
this.maxConcurrentJobAllocations, maxConcurrentJobAllocations);
|
||||||
this.maxConcurrentJobAllocations = maxConcurrentJobAllocations;
|
this.maxConcurrentJobAllocations = maxConcurrentJobAllocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setMaxMachineMemoryPercent(int maxMachineMemoryPercent) {
|
||||||
|
logger.info("Changing [{}] from [{}] to [{}]", MachineLearning.MAX_MACHINE_MEMORY_PERCENT.getKey(),
|
||||||
|
this.maxMachineMemoryPercent, maxMachineMemoryPercent);
|
||||||
|
this.maxMachineMemoryPercent = maxMachineMemoryPercent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -655,7 +666,7 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
}
|
}
|
||||||
|
|
||||||
static Assignment selectLeastLoadedMlNode(String jobId, ClusterState clusterState, int maxConcurrentJobAllocations,
|
static Assignment selectLeastLoadedMlNode(String jobId, ClusterState clusterState, int maxConcurrentJobAllocations,
|
||||||
int fallbackMaxNumberOfOpenJobs, Logger logger) {
|
int fallbackMaxNumberOfOpenJobs, int maxMachineMemoryPercent, Logger logger) {
|
||||||
List<String> unavailableIndices = verifyIndicesPrimaryShardsAreActive(jobId, clusterState);
|
List<String> unavailableIndices = verifyIndicesPrimaryShardsAreActive(jobId, clusterState);
|
||||||
if (unavailableIndices.size() != 0) {
|
if (unavailableIndices.size() != 0) {
|
||||||
String reason = "Not opening job [" + jobId + "], because not all primary shards are active for the following indices [" +
|
String reason = "Not opening job [" + jobId + "], because not all primary shards are active for the following indices [" +
|
||||||
|
@ -664,9 +675,14 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
return new Assignment(null, reason);
|
return new Assignment(null, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
long maxAvailable = Long.MIN_VALUE;
|
|
||||||
List<String> reasons = new LinkedList<>();
|
List<String> reasons = new LinkedList<>();
|
||||||
DiscoveryNode minLoadedNode = null;
|
long maxAvailableCount = Long.MIN_VALUE;
|
||||||
|
long maxAvailableMemory = Long.MIN_VALUE;
|
||||||
|
DiscoveryNode minLoadedNodeByCount = null;
|
||||||
|
DiscoveryNode minLoadedNodeByMemory = null;
|
||||||
|
// Try to allocate jobs according to memory usage, but if that's not possible (maybe due to a mixed version cluster or maybe
|
||||||
|
// because of some weird OS problem) then fall back to the old mechanism of only considering numbers of assigned jobs
|
||||||
|
boolean allocateByMemory = true;
|
||||||
PersistentTasksCustomMetaData persistentTasks = clusterState.getMetaData().custom(PersistentTasksCustomMetaData.TYPE);
|
PersistentTasksCustomMetaData persistentTasks = clusterState.getMetaData().custom(PersistentTasksCustomMetaData.TYPE);
|
||||||
for (DiscoveryNode node : clusterState.getNodes()) {
|
for (DiscoveryNode node : clusterState.getNodes()) {
|
||||||
Map<String, String> nodeAttributes = node.getAttributes();
|
Map<String, String> nodeAttributes = node.getAttributes();
|
||||||
|
@ -697,22 +713,26 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
long numberOfAssignedJobs;
|
long numberOfAssignedJobs = 0;
|
||||||
int numberOfAllocatingJobs;
|
int numberOfAllocatingJobs = 0;
|
||||||
|
long assignedJobMemory = 0;
|
||||||
if (persistentTasks != null) {
|
if (persistentTasks != null) {
|
||||||
numberOfAssignedJobs = persistentTasks.getNumberOfTasksOnNode(node.getId(), OpenJobAction.TASK_NAME);
|
// find all the job tasks assigned to this node
|
||||||
numberOfAllocatingJobs = persistentTasks.findTasks(OpenJobAction.TASK_NAME, task -> {
|
Collection<PersistentTask<?>> assignedTasks = persistentTasks.findTasks(OpenJobAction.TASK_NAME,
|
||||||
if (node.getId().equals(task.getExecutorNode()) == false) {
|
task -> node.getId().equals(task.getExecutorNode()));
|
||||||
return false;
|
numberOfAssignedJobs = assignedTasks.size();
|
||||||
|
for (PersistentTask<?> assignedTask : assignedTasks) {
|
||||||
|
JobTaskStatus jobTaskState = (JobTaskStatus) assignedTask.getStatus();
|
||||||
|
if (jobTaskState == null || // executor node didn't have the chance to set job status to OPENING
|
||||||
|
// previous executor node failed and current executor node didn't have the chance to set job status to OPENING
|
||||||
|
jobTaskState.isStatusStale(assignedTask)) {
|
||||||
|
++numberOfAllocatingJobs;
|
||||||
}
|
}
|
||||||
JobTaskStatus jobTaskState = (JobTaskStatus) task.getStatus();
|
String assignedJobId = ((JobParams) assignedTask.getParams()).getJobId();
|
||||||
return jobTaskState == null || // executor node didn't have the chance to set job status to OPENING
|
Job assignedJob = mlMetadata.getJobs().get(assignedJobId);
|
||||||
jobTaskState.isStatusStale(task); // previous executor node failed and
|
assert assignedJob != null;
|
||||||
// current executor node didn't have the chance to set job status to OPENING
|
assignedJobMemory += assignedJob.estimateMemoryFootprint();
|
||||||
}).size();
|
}
|
||||||
} else {
|
|
||||||
numberOfAssignedJobs = 0;
|
|
||||||
numberOfAllocatingJobs = 0;
|
|
||||||
}
|
}
|
||||||
if (numberOfAllocatingJobs >= maxConcurrentJobAllocations) {
|
if (numberOfAllocatingJobs >= maxConcurrentJobAllocations) {
|
||||||
String reason = "Not opening job [" + jobId + "] on node [" + node + "], because node exceeds [" + numberOfAllocatingJobs +
|
String reason = "Not opening job [" + jobId + "] on node [" + node + "], because node exceeds [" + numberOfAllocatingJobs +
|
||||||
|
@ -736,8 +756,8 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
long available = maxNumberOfOpenJobs - numberOfAssignedJobs;
|
long availableCount = maxNumberOfOpenJobs - numberOfAssignedJobs;
|
||||||
if (available == 0) {
|
if (availableCount == 0) {
|
||||||
String reason = "Not opening job [" + jobId + "] on node [" + node + "], because this node is full. " +
|
String reason = "Not opening job [" + jobId + "] on node [" + node + "], because this node is full. " +
|
||||||
"Number of opened jobs [" + numberOfAssignedJobs + "], " + MAX_OPEN_JOBS_PER_NODE.getKey() +
|
"Number of opened jobs [" + numberOfAssignedJobs + "], " + MAX_OPEN_JOBS_PER_NODE.getKey() +
|
||||||
" [" + maxNumberOfOpenJobs + "]";
|
" [" + maxNumberOfOpenJobs + "]";
|
||||||
|
@ -746,11 +766,55 @@ public class OpenJobAction extends Action<OpenJobAction.Request, OpenJobAction.R
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxAvailable < available) {
|
if (maxAvailableCount < availableCount) {
|
||||||
maxAvailable = available;
|
maxAvailableCount = availableCount;
|
||||||
minLoadedNode = node;
|
minLoadedNodeByCount = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
String machineMemoryStr = nodeAttributes.get(MachineLearning.MACHINE_MEMORY_NODE_ATTR);
|
||||||
|
long machineMemory = -1;
|
||||||
|
// TODO: remove leniency and reject the node if the attribute is null in 7.0
|
||||||
|
if (machineMemoryStr != null) {
|
||||||
|
try {
|
||||||
|
machineMemory = Long.parseLong(machineMemoryStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
String reason = "Not opening job [" + jobId + "] on node [" + node + "], because " +
|
||||||
|
MachineLearning.MACHINE_MEMORY_NODE_ATTR + " attribute [" + machineMemoryStr + "] is not a long";
|
||||||
|
logger.trace(reason);
|
||||||
|
reasons.add(reason);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allocateByMemory) {
|
||||||
|
if (machineMemory > 0) {
|
||||||
|
long maxMlMemory = machineMemory * maxMachineMemoryPercent / 100;
|
||||||
|
long estimatedMemoryFootprint = job.estimateMemoryFootprint();
|
||||||
|
long availableMemory = maxMlMemory - assignedJobMemory;
|
||||||
|
if (estimatedMemoryFootprint > availableMemory) {
|
||||||
|
String reason = "Not opening job [" + jobId + "] on node [" + node +
|
||||||
|
"], because this node has insufficient available memory. Available memory for ML [" + maxMlMemory +
|
||||||
|
"], memory required by existing jobs [" + assignedJobMemory +
|
||||||
|
"], estimated memory required for this job [" + estimatedMemoryFootprint + "]";
|
||||||
|
logger.trace(reason);
|
||||||
|
reasons.add(reason);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxAvailableMemory < availableMemory) {
|
||||||
|
maxAvailableMemory = availableMemory;
|
||||||
|
minLoadedNodeByMemory = node;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we cannot get the available memory on any machine in
|
||||||
|
// the cluster, fall back to simply allocating by job count
|
||||||
|
allocateByMemory = false;
|
||||||
|
logger.debug("Falling back to allocating job [{}] by job counts because machine memory was not available for node [{}]",
|
||||||
|
jobId, node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DiscoveryNode minLoadedNode = allocateByMemory ? minLoadedNodeByMemory : minLoadedNodeByCount;
|
||||||
if (minLoadedNode != null) {
|
if (minLoadedNode != null) {
|
||||||
logger.debug("selected node [{}] for job [{}]", minLoadedNode, jobId);
|
logger.debug("selected node [{}] for job [{}]", minLoadedNode, jobId);
|
||||||
return new Assignment(minLoadedNode.getId(), "");
|
return new Assignment(minLoadedNode.getId(), "");
|
||||||
|
|
|
@ -19,6 +19,9 @@ import org.elasticsearch.common.CheckedConsumer;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.xpack.ml.MachineLearning;
|
import org.elasticsearch.xpack.ml.MachineLearning;
|
||||||
import org.elasticsearch.xpack.ml.MlMetadata;
|
import org.elasticsearch.xpack.ml.MlMetadata;
|
||||||
import org.elasticsearch.xpack.ml.action.DeleteJobAction;
|
import org.elasticsearch.xpack.ml.action.DeleteJobAction;
|
||||||
|
@ -38,6 +41,7 @@ import org.elasticsearch.xpack.ml.notifications.Auditor;
|
||||||
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
|
import org.elasticsearch.xpack.ml.utils.ExceptionsHelper;
|
||||||
import org.elasticsearch.xpack.persistent.PersistentTasksCustomMetaData;
|
import org.elasticsearch.xpack.persistent.PersistentTasksCustomMetaData;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -245,6 +249,7 @@ public class JobManager extends AbstractComponent {
|
||||||
clusterService.submitStateUpdateTask("update-job-" + jobId,
|
clusterService.submitStateUpdateTask("update-job-" + jobId,
|
||||||
new AckedClusterStateUpdateTask<PutJobAction.Response>(request, actionListener) {
|
new AckedClusterStateUpdateTask<PutJobAction.Response>(request, actionListener) {
|
||||||
private volatile Job updatedJob;
|
private volatile Job updatedJob;
|
||||||
|
private volatile boolean changeWasRequired;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PutJobAction.Response newResponse(boolean acknowledged) {
|
protected PutJobAction.Response newResponse(boolean acknowledged) {
|
||||||
|
@ -255,16 +260,33 @@ public class JobManager extends AbstractComponent {
|
||||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||||
Job job = getJobOrThrowIfUnknown(jobId, currentState);
|
Job job = getJobOrThrowIfUnknown(jobId, currentState);
|
||||||
updatedJob = jobUpdate.mergeWithJob(job, maxModelMemoryLimit);
|
updatedJob = jobUpdate.mergeWithJob(job, maxModelMemoryLimit);
|
||||||
|
if (updatedJob.equals(job)) {
|
||||||
|
// nothing to do
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
changeWasRequired = true;
|
||||||
return updateClusterState(updatedJob, true, currentState);
|
return updateClusterState(updatedJob, true, currentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
PersistentTasksCustomMetaData persistentTasks =
|
if (changeWasRequired) {
|
||||||
newState.metaData().custom(PersistentTasksCustomMetaData.TYPE);
|
PersistentTasksCustomMetaData persistentTasks =
|
||||||
JobState jobState = MlMetadata.getJobState(jobId, persistentTasks);
|
newState.metaData().custom(PersistentTasksCustomMetaData.TYPE);
|
||||||
if (jobState == JobState.OPENED) {
|
JobState jobState = MlMetadata.getJobState(jobId, persistentTasks);
|
||||||
updateJobProcessNotifier.submitJobUpdate(jobUpdate);
|
if (jobState == JobState.OPENED) {
|
||||||
|
updateJobProcessNotifier.submitJobUpdate(jobUpdate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.debug("[{}] Ignored job update with no changes: {}", () -> jobId, () -> {
|
||||||
|
try {
|
||||||
|
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||||
|
jobUpdate.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||||
|
return jsonBuilder.string();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return "(unprintable due to " + e.getMessage() + ")";
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -330,9 +352,10 @@ public class JobManager extends AbstractComponent {
|
||||||
public void revertSnapshot(RevertModelSnapshotAction.Request request, ActionListener<RevertModelSnapshotAction.Response> actionListener,
|
public void revertSnapshot(RevertModelSnapshotAction.Request request, ActionListener<RevertModelSnapshotAction.Response> actionListener,
|
||||||
ModelSnapshot modelSnapshot) {
|
ModelSnapshot modelSnapshot) {
|
||||||
|
|
||||||
|
final ModelSizeStats modelSizeStats = modelSnapshot.getModelSizeStats();
|
||||||
final JobResultsPersister persister = new JobResultsPersister(settings, client);
|
final JobResultsPersister persister = new JobResultsPersister(settings, client);
|
||||||
|
|
||||||
// Step 2. After the model size stats is persisted, also persist the snapshot's quantiles and respond
|
// Step 3. After the model size stats is persisted, also persist the snapshot's quantiles and respond
|
||||||
// -------
|
// -------
|
||||||
CheckedConsumer<IndexResponse, Exception> modelSizeStatsResponseHandler = response -> {
|
CheckedConsumer<IndexResponse, Exception> modelSizeStatsResponseHandler = response -> {
|
||||||
persister.persistQuantiles(modelSnapshot.getQuantiles(), WriteRequest.RefreshPolicy.IMMEDIATE,
|
persister.persistQuantiles(modelSnapshot.getQuantiles(), WriteRequest.RefreshPolicy.IMMEDIATE,
|
||||||
|
@ -344,21 +367,20 @@ public class JobManager extends AbstractComponent {
|
||||||
}, actionListener::onFailure));
|
}, actionListener::onFailure));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 1. When the model_snapshot_id is updated on the job, persist the snapshot's model size stats with a touched log time
|
// Step 2. When the model_snapshot_id is updated on the job, persist the snapshot's model size stats with a touched log time
|
||||||
// so that a search for the latest model size stats returns the reverted one.
|
// so that a search for the latest model size stats returns the reverted one.
|
||||||
// -------
|
// -------
|
||||||
CheckedConsumer<Boolean, Exception> updateHandler = response -> {
|
CheckedConsumer<Boolean, Exception> updateHandler = response -> {
|
||||||
if (response) {
|
if (response) {
|
||||||
ModelSizeStats revertedModelSizeStats = new ModelSizeStats.Builder(modelSnapshot.getModelSizeStats())
|
ModelSizeStats revertedModelSizeStats = new ModelSizeStats.Builder(modelSizeStats).setLogTime(new Date()).build();
|
||||||
.setLogTime(new Date()).build();
|
|
||||||
persister.persistModelSizeStats(revertedModelSizeStats, WriteRequest.RefreshPolicy.IMMEDIATE, ActionListener.wrap(
|
persister.persistModelSizeStats(revertedModelSizeStats, WriteRequest.RefreshPolicy.IMMEDIATE, ActionListener.wrap(
|
||||||
modelSizeStatsResponseHandler::accept, actionListener::onFailure));
|
modelSizeStatsResponseHandler::accept, actionListener::onFailure));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 0. Kick off the chain of callbacks with the cluster state update
|
// Step 1. Do the cluster state update
|
||||||
// -------
|
// -------
|
||||||
clusterService.submitStateUpdateTask("revert-snapshot-" + request.getJobId(),
|
Consumer<Long> clusterStateHandler = response -> clusterService.submitStateUpdateTask("revert-snapshot-" + request.getJobId(),
|
||||||
new AckedClusterStateUpdateTask<Boolean>(request, ActionListener.wrap(updateHandler, actionListener::onFailure)) {
|
new AckedClusterStateUpdateTask<Boolean>(request, ActionListener.wrap(updateHandler, actionListener::onFailure)) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -377,9 +399,15 @@ public class JobManager extends AbstractComponent {
|
||||||
Job job = getJobOrThrowIfUnknown(request.getJobId(), currentState);
|
Job job = getJobOrThrowIfUnknown(request.getJobId(), currentState);
|
||||||
Job.Builder builder = new Job.Builder(job);
|
Job.Builder builder = new Job.Builder(job);
|
||||||
builder.setModelSnapshotId(modelSnapshot.getSnapshotId());
|
builder.setModelSnapshotId(modelSnapshot.getSnapshotId());
|
||||||
|
builder.setEstablishedModelMemory(response);
|
||||||
return updateClusterState(builder.build(), true, currentState);
|
return updateClusterState(builder.build(), true, currentState);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Step 0. Find the appropriate established model memory for the reverted job
|
||||||
|
// -------
|
||||||
|
jobProvider.getEstablishedMemoryUsage(request.getJobId(), modelSizeStats.getTimestamp(), modelSizeStats, clusterStateHandler,
|
||||||
|
actionListener::onFailure);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MlMetadata.Builder createMlMetadataBuilder(ClusterState currentState) {
|
private static MlMetadata.Builder createMlMetadataBuilder(ClusterState currentState) {
|
||||||
|
|
|
@ -24,18 +24,15 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import static org.elasticsearch.xpack.ml.action.UpdateProcessAction.Request;
|
import static org.elasticsearch.xpack.ml.action.UpdateProcessAction.Request;
|
||||||
import static org.elasticsearch.xpack.ml.action.UpdateProcessAction.Response;
|
import static org.elasticsearch.xpack.ml.action.UpdateProcessAction.Response;
|
||||||
|
|
||||||
public class UpdateJobProcessNotifier extends AbstractComponent
|
public class UpdateJobProcessNotifier extends AbstractComponent implements LocalNodeMasterListener {
|
||||||
implements LocalNodeMasterListener {
|
|
||||||
|
|
||||||
private final Client client;
|
private final Client client;
|
||||||
private final ThreadPool threadPool;
|
private final ThreadPool threadPool;
|
||||||
private final LinkedBlockingQueue<JobUpdate> orderedJobUpdates =
|
private final LinkedBlockingQueue<JobUpdate> orderedJobUpdates = new LinkedBlockingQueue<>(1000);
|
||||||
new LinkedBlockingQueue<>(1000);
|
|
||||||
|
|
||||||
private volatile ThreadPool.Cancellable cancellable;
|
private volatile ThreadPool.Cancellable cancellable;
|
||||||
|
|
||||||
public UpdateJobProcessNotifier(Settings settings, Client client,
|
public UpdateJobProcessNotifier(Settings settings, Client client, ClusterService clusterService, ThreadPool threadPool) {
|
||||||
ClusterService clusterService, ThreadPool threadPool) {
|
|
||||||
super(settings);
|
super(settings);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.threadPool = threadPool;
|
this.threadPool = threadPool;
|
||||||
|
@ -62,12 +59,11 @@ public class UpdateJobProcessNotifier extends AbstractComponent
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() {
|
private void start() {
|
||||||
cancellable = threadPool.scheduleWithFixedDelay(this::processNextUpdate,
|
cancellable = threadPool.scheduleWithFixedDelay(this::processNextUpdate, TimeValue.timeValueSeconds(1), ThreadPool.Names.GENERIC);
|
||||||
TimeValue.timeValueSeconds(1), ThreadPool.Names.GENERIC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() {
|
private void stop() {
|
||||||
orderedJobUpdates.clear();
|
orderedJobUpdates.clear();
|
||||||
|
|
||||||
ThreadPool.Cancellable cancellable = this.cancellable;
|
ThreadPool.Cancellable cancellable = this.cancellable;
|
||||||
|
@ -82,20 +78,26 @@ public class UpdateJobProcessNotifier extends AbstractComponent
|
||||||
return ThreadPool.Names.SAME;
|
return ThreadPool.Names.SAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processNextUpdate() {
|
private void processNextUpdate() {
|
||||||
try {
|
try {
|
||||||
JobUpdate jobUpdate = orderedJobUpdates.poll();
|
JobUpdate jobUpdate = orderedJobUpdates.poll();
|
||||||
if (jobUpdate != null) {
|
if (jobUpdate != null) {
|
||||||
executeRemoteJob(jobUpdate);
|
executeRemoteJobIfNecessary(jobUpdate);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Unable while processing next job update", e);
|
logger.error("Unable while processing next job update", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void executeRemoteJobIfNecessary(JobUpdate update) {
|
||||||
|
// Do nothing if the fields that the C++ needs aren't being updated
|
||||||
|
if (update.isAutodetectProcessUpdate()) {
|
||||||
|
executeRemoteJob(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void executeRemoteJob(JobUpdate update) {
|
void executeRemoteJob(JobUpdate update) {
|
||||||
Request request = new Request(update.getJobId(), update.getModelPlotConfig(),
|
Request request = new Request(update.getJobId(), update.getModelPlotConfig(), update.getDetectorUpdates());
|
||||||
update.getDetectorUpdates());
|
|
||||||
client.execute(UpdateProcessAction.INSTANCE, request,
|
client.execute(UpdateProcessAction.INSTANCE, request,
|
||||||
new ActionListener<Response>() {
|
new ActionListener<Response>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,15 +112,12 @@ public class UpdateJobProcessNotifier extends AbstractComponent
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
if (e instanceof ResourceNotFoundException) {
|
if (e instanceof ResourceNotFoundException) {
|
||||||
logger.debug("Remote job [{}] not updated as it has been deleted",
|
logger.debug("Remote job [{}] not updated as it has been deleted", update.getJobId());
|
||||||
update.getJobId());
|
} else if (e.getMessage().contains("because job [" + update.getJobId() + "] is not open")
|
||||||
} else if (e.getMessage().contains("because job [" + update.getJobId() +
|
&& e instanceof ElasticsearchStatusException) {
|
||||||
"] is not open") && e instanceof ElasticsearchStatusException) {
|
logger.debug("Remote job [{}] not updated as it is no longer open", update.getJobId());
|
||||||
logger.debug("Remote job [{}] not updated as it is no longer open",
|
|
||||||
update.getJobId());
|
|
||||||
} else {
|
} else {
|
||||||
logger.error("Failed to update remote job [" + update.getJobId() + "]",
|
logger.error("Failed to update remote job [" + update.getJobId() + "]", e);
|
||||||
e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,7 +10,6 @@ import org.elasticsearch.cluster.AbstractDiffable;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.inject.spi.Message;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
@ -69,6 +68,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
public static final ParseField DESCRIPTION = new ParseField("description");
|
public static final ParseField DESCRIPTION = new ParseField("description");
|
||||||
public static final ParseField FINISHED_TIME = new ParseField("finished_time");
|
public static final ParseField FINISHED_TIME = new ParseField("finished_time");
|
||||||
public static final ParseField LAST_DATA_TIME = new ParseField("last_data_time");
|
public static final ParseField LAST_DATA_TIME = new ParseField("last_data_time");
|
||||||
|
public static final ParseField ESTABLISHED_MODEL_MEMORY = new ParseField("established_model_memory");
|
||||||
public static final ParseField MODEL_PLOT_CONFIG = new ParseField("model_plot_config");
|
public static final ParseField MODEL_PLOT_CONFIG = new ParseField("model_plot_config");
|
||||||
public static final ParseField RENORMALIZATION_WINDOW_DAYS = new ParseField("renormalization_window_days");
|
public static final ParseField RENORMALIZATION_WINDOW_DAYS = new ParseField("renormalization_window_days");
|
||||||
public static final ParseField BACKGROUND_PERSIST_INTERVAL = new ParseField("background_persist_interval");
|
public static final ParseField BACKGROUND_PERSIST_INTERVAL = new ParseField("background_persist_interval");
|
||||||
|
@ -88,6 +88,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
|
|
||||||
public static final int MAX_JOB_ID_LENGTH = 64;
|
public static final int MAX_JOB_ID_LENGTH = 64;
|
||||||
public static final TimeValue MIN_BACKGROUND_PERSIST_INTERVAL = TimeValue.timeValueHours(1);
|
public static final TimeValue MIN_BACKGROUND_PERSIST_INTERVAL = TimeValue.timeValueHours(1);
|
||||||
|
public static final ByteSizeValue PROCESS_MEMORY_OVERHEAD = new ByteSizeValue(100, ByteSizeUnit.MB);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PARSERS.put(MlParserType.METADATA, METADATA_PARSER);
|
PARSERS.put(MlParserType.METADATA, METADATA_PARSER);
|
||||||
|
@ -127,6 +128,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"unexpected token [" + p.currentToken() + "] for [" + LAST_DATA_TIME.getPreferredName() + "]");
|
"unexpected token [" + p.currentToken() + "] for [" + LAST_DATA_TIME.getPreferredName() + "]");
|
||||||
}, LAST_DATA_TIME, ValueType.VALUE);
|
}, LAST_DATA_TIME, ValueType.VALUE);
|
||||||
|
parser.declareLong(Builder::setEstablishedModelMemory, ESTABLISHED_MODEL_MEMORY);
|
||||||
parser.declareObject(Builder::setAnalysisConfig, AnalysisConfig.PARSERS.get(parserType), ANALYSIS_CONFIG);
|
parser.declareObject(Builder::setAnalysisConfig, AnalysisConfig.PARSERS.get(parserType), ANALYSIS_CONFIG);
|
||||||
parser.declareObject(Builder::setAnalysisLimits, AnalysisLimits.PARSERS.get(parserType), ANALYSIS_LIMITS);
|
parser.declareObject(Builder::setAnalysisLimits, AnalysisLimits.PARSERS.get(parserType), ANALYSIS_LIMITS);
|
||||||
parser.declareObject(Builder::setDataDescription, DataDescription.PARSERS.get(parserType), DATA_DESCRIPTION);
|
parser.declareObject(Builder::setDataDescription, DataDescription.PARSERS.get(parserType), DATA_DESCRIPTION);
|
||||||
|
@ -159,6 +161,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
private final Date createTime;
|
private final Date createTime;
|
||||||
private final Date finishedTime;
|
private final Date finishedTime;
|
||||||
private final Date lastDataTime;
|
private final Date lastDataTime;
|
||||||
|
private final Long establishedModelMemory;
|
||||||
private final AnalysisConfig analysisConfig;
|
private final AnalysisConfig analysisConfig;
|
||||||
private final AnalysisLimits analysisLimits;
|
private final AnalysisLimits analysisLimits;
|
||||||
private final DataDescription dataDescription;
|
private final DataDescription dataDescription;
|
||||||
|
@ -173,7 +176,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
private final boolean deleted;
|
private final boolean deleted;
|
||||||
|
|
||||||
private Job(String jobId, String jobType, Version jobVersion, List<String> groups, String description, Date createTime,
|
private Job(String jobId, String jobType, Version jobVersion, List<String> groups, String description, Date createTime,
|
||||||
Date finishedTime, Date lastDataTime,
|
Date finishedTime, Date lastDataTime, Long establishedModelMemory,
|
||||||
AnalysisConfig analysisConfig, AnalysisLimits analysisLimits, DataDescription dataDescription,
|
AnalysisConfig analysisConfig, AnalysisLimits analysisLimits, DataDescription dataDescription,
|
||||||
ModelPlotConfig modelPlotConfig, Long renormalizationWindowDays, TimeValue backgroundPersistInterval,
|
ModelPlotConfig modelPlotConfig, Long renormalizationWindowDays, TimeValue backgroundPersistInterval,
|
||||||
Long modelSnapshotRetentionDays, Long resultsRetentionDays, Map<String, Object> customSettings,
|
Long modelSnapshotRetentionDays, Long resultsRetentionDays, Map<String, Object> customSettings,
|
||||||
|
@ -187,6 +190,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
this.createTime = createTime;
|
this.createTime = createTime;
|
||||||
this.finishedTime = finishedTime;
|
this.finishedTime = finishedTime;
|
||||||
this.lastDataTime = lastDataTime;
|
this.lastDataTime = lastDataTime;
|
||||||
|
this.establishedModelMemory = establishedModelMemory;
|
||||||
this.analysisConfig = analysisConfig;
|
this.analysisConfig = analysisConfig;
|
||||||
this.analysisLimits = analysisLimits;
|
this.analysisLimits = analysisLimits;
|
||||||
this.dataDescription = dataDescription;
|
this.dataDescription = dataDescription;
|
||||||
|
@ -218,6 +222,12 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
createTime = new Date(in.readVLong());
|
createTime = new Date(in.readVLong());
|
||||||
finishedTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
finishedTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
||||||
lastDataTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
lastDataTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
||||||
|
// TODO: set to V_6_1_0 after backporting
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
establishedModelMemory = in.readOptionalLong();
|
||||||
|
} else {
|
||||||
|
establishedModelMemory = null;
|
||||||
|
}
|
||||||
analysisConfig = new AnalysisConfig(in);
|
analysisConfig = new AnalysisConfig(in);
|
||||||
analysisLimits = in.readOptionalWriteable(AnalysisLimits::new);
|
analysisLimits = in.readOptionalWriteable(AnalysisLimits::new);
|
||||||
dataDescription = in.readOptionalWriteable(DataDescription::new);
|
dataDescription = in.readOptionalWriteable(DataDescription::new);
|
||||||
|
@ -319,6 +329,16 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
return lastDataTime;
|
return lastDataTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The established model memory of the job, or <code>null</code> if model
|
||||||
|
* memory has not reached equilibrium yet.
|
||||||
|
*
|
||||||
|
* @return The established model memory of the job
|
||||||
|
*/
|
||||||
|
public Long getEstablishedModelMemory() {
|
||||||
|
return establishedModelMemory;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The analysis configuration object
|
* The analysis configuration object
|
||||||
*
|
*
|
||||||
|
@ -418,6 +438,23 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
return new ArrayList<>(allFields);
|
return new ArrayList<>(allFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a best estimate of the job's memory footprint using the information available.
|
||||||
|
* If a job has an established model memory size, then this is the best estimate.
|
||||||
|
* Otherwise, assume the maximum model memory limit will eventually be required.
|
||||||
|
* In either case, a fixed overhead is added to account for the memory required by the
|
||||||
|
* program code and stack.
|
||||||
|
* @return an estimate of the memory requirement of this job, in bytes
|
||||||
|
*/
|
||||||
|
public long estimateMemoryFootprint() {
|
||||||
|
if (establishedModelMemory != null && establishedModelMemory > 0) {
|
||||||
|
return establishedModelMemory + PROCESS_MEMORY_OVERHEAD.getBytes();
|
||||||
|
}
|
||||||
|
Long modelMemoryLimit = (analysisLimits != null) ? analysisLimits.getModelMemoryLimit() : null;
|
||||||
|
return ByteSizeUnit.MB.toBytes((modelMemoryLimit != null) ? modelMemoryLimit : JobUpdate.UNDEFINED_MODEL_MEMORY_LIMIT_DEFAULT)
|
||||||
|
+ PROCESS_MEMORY_OVERHEAD.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
out.writeString(jobId);
|
out.writeString(jobId);
|
||||||
|
@ -447,6 +484,10 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
} else {
|
} else {
|
||||||
out.writeBoolean(false);
|
out.writeBoolean(false);
|
||||||
}
|
}
|
||||||
|
// TODO: set to V_6_1_0 after backporting
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
out.writeOptionalLong(establishedModelMemory);
|
||||||
|
}
|
||||||
analysisConfig.writeTo(out);
|
analysisConfig.writeTo(out);
|
||||||
out.writeOptionalWriteable(analysisLimits);
|
out.writeOptionalWriteable(analysisLimits);
|
||||||
out.writeOptionalWriteable(dataDescription);
|
out.writeOptionalWriteable(dataDescription);
|
||||||
|
@ -492,6 +533,9 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
builder.dateField(LAST_DATA_TIME.getPreferredName(), LAST_DATA_TIME.getPreferredName() + humanReadableSuffix,
|
builder.dateField(LAST_DATA_TIME.getPreferredName(), LAST_DATA_TIME.getPreferredName() + humanReadableSuffix,
|
||||||
lastDataTime.getTime());
|
lastDataTime.getTime());
|
||||||
}
|
}
|
||||||
|
if (establishedModelMemory != null) {
|
||||||
|
builder.field(ESTABLISHED_MODEL_MEMORY.getPreferredName(), establishedModelMemory);
|
||||||
|
}
|
||||||
builder.field(ANALYSIS_CONFIG.getPreferredName(), analysisConfig, params);
|
builder.field(ANALYSIS_CONFIG.getPreferredName(), analysisConfig, params);
|
||||||
if (analysisLimits != null) {
|
if (analysisLimits != null) {
|
||||||
builder.field(ANALYSIS_LIMITS.getPreferredName(), analysisLimits, params);
|
builder.field(ANALYSIS_LIMITS.getPreferredName(), analysisLimits, params);
|
||||||
|
@ -546,6 +590,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
&& Objects.equals(this.createTime, that.createTime)
|
&& Objects.equals(this.createTime, that.createTime)
|
||||||
&& Objects.equals(this.finishedTime, that.finishedTime)
|
&& Objects.equals(this.finishedTime, that.finishedTime)
|
||||||
&& Objects.equals(this.lastDataTime, that.lastDataTime)
|
&& Objects.equals(this.lastDataTime, that.lastDataTime)
|
||||||
|
&& Objects.equals(this.establishedModelMemory, that.establishedModelMemory)
|
||||||
&& Objects.equals(this.analysisConfig, that.analysisConfig)
|
&& Objects.equals(this.analysisConfig, that.analysisConfig)
|
||||||
&& Objects.equals(this.analysisLimits, that.analysisLimits) && Objects.equals(this.dataDescription, that.dataDescription)
|
&& Objects.equals(this.analysisLimits, that.analysisLimits) && Objects.equals(this.dataDescription, that.dataDescription)
|
||||||
&& Objects.equals(this.modelPlotConfig, that.modelPlotConfig)
|
&& Objects.equals(this.modelPlotConfig, that.modelPlotConfig)
|
||||||
|
@ -561,8 +606,8 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(jobId, jobType, jobVersion, groups, description, createTime, finishedTime, lastDataTime, analysisConfig,
|
return Objects.hash(jobId, jobType, jobVersion, groups, description, createTime, finishedTime, lastDataTime, establishedModelMemory,
|
||||||
analysisLimits, dataDescription, modelPlotConfig, renormalizationWindowDays,
|
analysisConfig, analysisLimits, dataDescription, modelPlotConfig, renormalizationWindowDays,
|
||||||
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, customSettings,
|
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, customSettings,
|
||||||
modelSnapshotId, resultsIndexName, deleted);
|
modelSnapshotId, resultsIndexName, deleted);
|
||||||
}
|
}
|
||||||
|
@ -605,6 +650,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
private Date finishedTime;
|
private Date finishedTime;
|
||||||
private Date lastDataTime;
|
private Date lastDataTime;
|
||||||
|
private Long establishedModelMemory;
|
||||||
private ModelPlotConfig modelPlotConfig;
|
private ModelPlotConfig modelPlotConfig;
|
||||||
private Long renormalizationWindowDays;
|
private Long renormalizationWindowDays;
|
||||||
private TimeValue backgroundPersistInterval;
|
private TimeValue backgroundPersistInterval;
|
||||||
|
@ -634,6 +680,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
this.createTime = job.getCreateTime();
|
this.createTime = job.getCreateTime();
|
||||||
this.finishedTime = job.getFinishedTime();
|
this.finishedTime = job.getFinishedTime();
|
||||||
this.lastDataTime = job.getLastDataTime();
|
this.lastDataTime = job.getLastDataTime();
|
||||||
|
this.establishedModelMemory = job.getEstablishedModelMemory();
|
||||||
this.modelPlotConfig = job.getModelPlotConfig();
|
this.modelPlotConfig = job.getModelPlotConfig();
|
||||||
this.renormalizationWindowDays = job.getRenormalizationWindowDays();
|
this.renormalizationWindowDays = job.getRenormalizationWindowDays();
|
||||||
this.backgroundPersistInterval = job.getBackgroundPersistInterval();
|
this.backgroundPersistInterval = job.getBackgroundPersistInterval();
|
||||||
|
@ -660,6 +707,10 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
createTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
createTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
||||||
finishedTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
finishedTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
||||||
lastDataTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
lastDataTime = in.readBoolean() ? new Date(in.readVLong()) : null;
|
||||||
|
// TODO: set to V_6_1_0 after backporting
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
establishedModelMemory = in.readOptionalLong();
|
||||||
|
}
|
||||||
analysisConfig = in.readOptionalWriteable(AnalysisConfig::new);
|
analysisConfig = in.readOptionalWriteable(AnalysisConfig::new);
|
||||||
analysisLimits = in.readOptionalWriteable(AnalysisLimits::new);
|
analysisLimits = in.readOptionalWriteable(AnalysisLimits::new);
|
||||||
dataDescription = in.readOptionalWriteable(DataDescription::new);
|
dataDescription = in.readOptionalWriteable(DataDescription::new);
|
||||||
|
@ -699,10 +750,6 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
this.groups = groups == null ? Collections.emptyList() : groups;
|
this.groups = groups == null ? Collections.emptyList() : groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getCreateTime() {
|
|
||||||
return createTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder setCustomSettings(Map<String, Object> customSettings) {
|
public Builder setCustomSettings(Map<String, Object> customSettings) {
|
||||||
this.customSettings = customSettings;
|
this.customSettings = customSettings;
|
||||||
return this;
|
return this;
|
||||||
|
@ -742,6 +789,11 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setEstablishedModelMemory(Long establishedModelMemory) {
|
||||||
|
this.establishedModelMemory = establishedModelMemory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder setDataDescription(DataDescription.Builder description) {
|
public Builder setDataDescription(DataDescription.Builder description) {
|
||||||
dataDescription = ExceptionsHelper.requireNonNull(description, DATA_DESCRIPTION.getPreferredName()).build();
|
dataDescription = ExceptionsHelper.requireNonNull(description, DATA_DESCRIPTION.getPreferredName()).build();
|
||||||
return this;
|
return this;
|
||||||
|
@ -844,6 +896,10 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
} else {
|
} else {
|
||||||
out.writeBoolean(false);
|
out.writeBoolean(false);
|
||||||
}
|
}
|
||||||
|
// TODO: set to V_6_1_0 after backporting
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
out.writeOptionalLong(establishedModelMemory);
|
||||||
|
}
|
||||||
out.writeOptionalWriteable(analysisConfig);
|
out.writeOptionalWriteable(analysisConfig);
|
||||||
out.writeOptionalWriteable(analysisLimits);
|
out.writeOptionalWriteable(analysisLimits);
|
||||||
out.writeOptionalWriteable(dataDescription);
|
out.writeOptionalWriteable(dataDescription);
|
||||||
|
@ -880,6 +936,9 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
if (lastDataTime != null) {
|
if (lastDataTime != null) {
|
||||||
builder.field(LAST_DATA_TIME.getPreferredName(), lastDataTime.getTime());
|
builder.field(LAST_DATA_TIME.getPreferredName(), lastDataTime.getTime());
|
||||||
}
|
}
|
||||||
|
if (establishedModelMemory != null) {
|
||||||
|
builder.field(ESTABLISHED_MODEL_MEMORY.getPreferredName(), establishedModelMemory);
|
||||||
|
}
|
||||||
if (analysisConfig != null) {
|
if (analysisConfig != null) {
|
||||||
builder.field(ANALYSIS_CONFIG.getPreferredName(), analysisConfig, params);
|
builder.field(ANALYSIS_CONFIG.getPreferredName(), analysisConfig, params);
|
||||||
}
|
}
|
||||||
|
@ -937,6 +996,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
&& Objects.equals(this.createTime, that.createTime)
|
&& Objects.equals(this.createTime, that.createTime)
|
||||||
&& Objects.equals(this.finishedTime, that.finishedTime)
|
&& Objects.equals(this.finishedTime, that.finishedTime)
|
||||||
&& Objects.equals(this.lastDataTime, that.lastDataTime)
|
&& Objects.equals(this.lastDataTime, that.lastDataTime)
|
||||||
|
&& Objects.equals(this.establishedModelMemory, that.establishedModelMemory)
|
||||||
&& Objects.equals(this.modelPlotConfig, that.modelPlotConfig)
|
&& Objects.equals(this.modelPlotConfig, that.modelPlotConfig)
|
||||||
&& Objects.equals(this.renormalizationWindowDays, that.renormalizationWindowDays)
|
&& Objects.equals(this.renormalizationWindowDays, that.renormalizationWindowDays)
|
||||||
&& Objects.equals(this.backgroundPersistInterval, that.backgroundPersistInterval)
|
&& Objects.equals(this.backgroundPersistInterval, that.backgroundPersistInterval)
|
||||||
|
@ -951,8 +1011,9 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id, jobType, jobVersion, description, analysisConfig, analysisLimits, dataDescription, createTime,
|
return Objects.hash(id, jobType, jobVersion, description, analysisConfig, analysisLimits, dataDescription, createTime,
|
||||||
finishedTime, lastDataTime, modelPlotConfig, renormalizationWindowDays, backgroundPersistInterval,
|
finishedTime, lastDataTime, establishedModelMemory, modelPlotConfig, renormalizationWindowDays,
|
||||||
modelSnapshotRetentionDays, resultsRetentionDays, customSettings, modelSnapshotId, resultsIndexName, deleted);
|
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, customSettings, modelSnapshotId,
|
||||||
|
resultsIndexName, deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1048,6 +1109,11 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
public Job build(Date createTime) {
|
public Job build(Date createTime) {
|
||||||
setCreateTime(createTime);
|
setCreateTime(createTime);
|
||||||
setJobVersion(Version.CURRENT);
|
setJobVersion(Version.CURRENT);
|
||||||
|
// TODO: Maybe we _could_ accept a value for this supplied at create time - it would
|
||||||
|
// mean cloned jobs that hadn't been edited much would start with an accurate expected size.
|
||||||
|
// But on the other hand it would mean jobs that were cloned and then completely changed
|
||||||
|
// would start with a size that was completely wrong.
|
||||||
|
setEstablishedModelMemory(null);
|
||||||
return build();
|
return build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1076,7 +1142,7 @@ public class Job extends AbstractDiffable<Job> implements Writeable, ToXContentO
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Job(
|
return new Job(
|
||||||
id, jobType, jobVersion, groups, description, createTime, finishedTime, lastDataTime,
|
id, jobType, jobVersion, groups, description, createTime, finishedTime, lastDataTime, establishedModelMemory,
|
||||||
analysisConfig, analysisLimits, dataDescription, modelPlotConfig, renormalizationWindowDays,
|
analysisConfig, analysisLimits, dataDescription, modelPlotConfig, renormalizationWindowDays,
|
||||||
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, customSettings,
|
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, customSettings,
|
||||||
modelSnapshotId, resultsIndexName, deleted);
|
modelSnapshotId, resultsIndexName, deleted);
|
||||||
|
|
|
@ -48,6 +48,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
PARSER.declareStringArray(Builder::setCategorizationFilters, AnalysisConfig.CATEGORIZATION_FILTERS);
|
PARSER.declareStringArray(Builder::setCategorizationFilters, AnalysisConfig.CATEGORIZATION_FILTERS);
|
||||||
PARSER.declareField(Builder::setCustomSettings, (p, c) -> p.map(), Job.CUSTOM_SETTINGS, ObjectParser.ValueType.OBJECT);
|
PARSER.declareField(Builder::setCustomSettings, (p, c) -> p.map(), Job.CUSTOM_SETTINGS, ObjectParser.ValueType.OBJECT);
|
||||||
PARSER.declareString(Builder::setModelSnapshotId, Job.MODEL_SNAPSHOT_ID);
|
PARSER.declareString(Builder::setModelSnapshotId, Job.MODEL_SNAPSHOT_ID);
|
||||||
|
PARSER.declareLong(Builder::setEstablishedModelMemory, Job.ESTABLISHED_MODEL_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,7 +57,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
* If model_memory_limit is not defined for a job then the
|
* If model_memory_limit is not defined for a job then the
|
||||||
* job was created before 6.1 and a value of 4GB is assumed.
|
* job was created before 6.1 and a value of 4GB is assumed.
|
||||||
*/
|
*/
|
||||||
private static final long UNDEFINED_MODEL_MEMORY_LIMIT_DEFAULT = 4096;
|
static final long UNDEFINED_MODEL_MEMORY_LIMIT_DEFAULT = 4096;
|
||||||
|
|
||||||
private final String jobId;
|
private final String jobId;
|
||||||
private final List<String> groups;
|
private final List<String> groups;
|
||||||
|
@ -71,13 +72,15 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
private final List<String> categorizationFilters;
|
private final List<String> categorizationFilters;
|
||||||
private final Map<String, Object> customSettings;
|
private final Map<String, Object> customSettings;
|
||||||
private final String modelSnapshotId;
|
private final String modelSnapshotId;
|
||||||
|
private final Long establishedModelMemory;
|
||||||
|
|
||||||
private JobUpdate(String jobId, @Nullable List<String> groups, @Nullable String description,
|
private JobUpdate(String jobId, @Nullable List<String> groups, @Nullable String description,
|
||||||
@Nullable List<DetectorUpdate> detectorUpdates, @Nullable ModelPlotConfig modelPlotConfig,
|
@Nullable List<DetectorUpdate> detectorUpdates, @Nullable ModelPlotConfig modelPlotConfig,
|
||||||
@Nullable AnalysisLimits analysisLimits, @Nullable TimeValue backgroundPersistInterval,
|
@Nullable AnalysisLimits analysisLimits, @Nullable TimeValue backgroundPersistInterval,
|
||||||
@Nullable Long renormalizationWindowDays, @Nullable Long resultsRetentionDays,
|
@Nullable Long renormalizationWindowDays, @Nullable Long resultsRetentionDays,
|
||||||
@Nullable Long modelSnapshotRetentionDays, @Nullable List<String> categorisationFilters,
|
@Nullable Long modelSnapshotRetentionDays, @Nullable List<String> categorisationFilters,
|
||||||
@Nullable Map<String, Object> customSettings, @Nullable String modelSnapshotId) {
|
@Nullable Map<String, Object> customSettings, @Nullable String modelSnapshotId,
|
||||||
|
@Nullable Long establishedModelMemory) {
|
||||||
this.jobId = jobId;
|
this.jobId = jobId;
|
||||||
this.groups = groups;
|
this.groups = groups;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
@ -91,6 +94,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
this.categorizationFilters = categorisationFilters;
|
this.categorizationFilters = categorisationFilters;
|
||||||
this.customSettings = customSettings;
|
this.customSettings = customSettings;
|
||||||
this.modelSnapshotId = modelSnapshotId;
|
this.modelSnapshotId = modelSnapshotId;
|
||||||
|
this.establishedModelMemory = establishedModelMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JobUpdate(StreamInput in) throws IOException {
|
public JobUpdate(StreamInput in) throws IOException {
|
||||||
|
@ -120,6 +124,12 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
}
|
}
|
||||||
customSettings = in.readMap();
|
customSettings = in.readMap();
|
||||||
modelSnapshotId = in.readOptionalString();
|
modelSnapshotId = in.readOptionalString();
|
||||||
|
// TODO: set to V_6_1_0 after backporting
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
establishedModelMemory = in.readOptionalLong();
|
||||||
|
} else {
|
||||||
|
establishedModelMemory = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -146,6 +156,10 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
}
|
}
|
||||||
out.writeMap(customSettings);
|
out.writeMap(customSettings);
|
||||||
out.writeOptionalString(modelSnapshotId);
|
out.writeOptionalString(modelSnapshotId);
|
||||||
|
// TODO: set to V_6_1_0 after backporting
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
|
||||||
|
out.writeOptionalLong(establishedModelMemory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getJobId() {
|
public String getJobId() {
|
||||||
|
@ -200,6 +214,10 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
return modelSnapshotId;
|
return modelSnapshotId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getEstablishedModelMemory() {
|
||||||
|
return establishedModelMemory;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAutodetectProcessUpdate() {
|
public boolean isAutodetectProcessUpdate() {
|
||||||
return modelPlotConfig != null || detectorUpdates != null;
|
return modelPlotConfig != null || detectorUpdates != null;
|
||||||
}
|
}
|
||||||
|
@ -244,6 +262,9 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
if (modelSnapshotId != null) {
|
if (modelSnapshotId != null) {
|
||||||
builder.field(Job.MODEL_SNAPSHOT_ID.getPreferredName(), modelSnapshotId);
|
builder.field(Job.MODEL_SNAPSHOT_ID.getPreferredName(), modelSnapshotId);
|
||||||
}
|
}
|
||||||
|
if (establishedModelMemory != null) {
|
||||||
|
builder.field(Job.ESTABLISHED_MODEL_MEMORY.getPreferredName(), establishedModelMemory);
|
||||||
|
}
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
@ -344,7 +365,14 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
if (modelSnapshotId != null) {
|
if (modelSnapshotId != null) {
|
||||||
builder.setModelSnapshotId(modelSnapshotId);
|
builder.setModelSnapshotId(modelSnapshotId);
|
||||||
}
|
}
|
||||||
|
if (establishedModelMemory != null) {
|
||||||
|
// An established model memory of zero means we don't actually know the established model memory
|
||||||
|
if (establishedModelMemory > 0) {
|
||||||
|
builder.setEstablishedModelMemory(establishedModelMemory);
|
||||||
|
} else {
|
||||||
|
builder.setEstablishedModelMemory(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,14 +400,15 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
&& Objects.equals(this.resultsRetentionDays, that.resultsRetentionDays)
|
&& Objects.equals(this.resultsRetentionDays, that.resultsRetentionDays)
|
||||||
&& Objects.equals(this.categorizationFilters, that.categorizationFilters)
|
&& Objects.equals(this.categorizationFilters, that.categorizationFilters)
|
||||||
&& Objects.equals(this.customSettings, that.customSettings)
|
&& Objects.equals(this.customSettings, that.customSettings)
|
||||||
&& Objects.equals(this.modelSnapshotId, that.modelSnapshotId);
|
&& Objects.equals(this.modelSnapshotId, that.modelSnapshotId)
|
||||||
|
&& Objects.equals(this.establishedModelMemory, that.establishedModelMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(jobId, groups, description, detectorUpdates, modelPlotConfig, analysisLimits, renormalizationWindowDays,
|
return Objects.hash(jobId, groups, description, detectorUpdates, modelPlotConfig, analysisLimits, renormalizationWindowDays,
|
||||||
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, categorizationFilters, customSettings,
|
backgroundPersistInterval, modelSnapshotRetentionDays, resultsRetentionDays, categorizationFilters, customSettings,
|
||||||
modelSnapshotId);
|
modelSnapshotId, establishedModelMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DetectorUpdate implements Writeable, ToXContentObject {
|
public static class DetectorUpdate implements Writeable, ToXContentObject {
|
||||||
|
@ -490,6 +519,7 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
private List<String> categorizationFilters;
|
private List<String> categorizationFilters;
|
||||||
private Map<String, Object> customSettings;
|
private Map<String, Object> customSettings;
|
||||||
private String modelSnapshotId;
|
private String modelSnapshotId;
|
||||||
|
private Long establishedModelMemory;
|
||||||
|
|
||||||
public Builder(String jobId) {
|
public Builder(String jobId) {
|
||||||
this.jobId = jobId;
|
this.jobId = jobId;
|
||||||
|
@ -560,10 +590,15 @@ public class JobUpdate implements Writeable, ToXContentObject {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setEstablishedModelMemory(Long establishedModelMemory) {
|
||||||
|
this.establishedModelMemory = establishedModelMemory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public JobUpdate build() {
|
public JobUpdate build() {
|
||||||
return new JobUpdate(jobId, groups, description, detectorUpdates, modelPlotConfig, analysisLimits, backgroundPersistInterval,
|
return new JobUpdate(jobId, groups, description, detectorUpdates, modelPlotConfig, analysisLimits, backgroundPersistInterval,
|
||||||
renormalizationWindowDays, resultsRetentionDays, modelSnapshotRetentionDays, categorizationFilters, customSettings,
|
renormalizationWindowDays, resultsRetentionDays, modelSnapshotRetentionDays, categorizationFilters, customSettings,
|
||||||
modelSnapshotId);
|
modelSnapshotId, establishedModelMemory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -92,7 +93,7 @@ public class JobProvider {
|
||||||
private static final Logger LOGGER = Loggers.getLogger(JobProvider.class);
|
private static final Logger LOGGER = Loggers.getLogger(JobProvider.class);
|
||||||
|
|
||||||
private static final int RECORDS_SIZE_PARAM = 10000;
|
private static final int RECORDS_SIZE_PARAM = 10000;
|
||||||
private static final int BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE = 20;
|
public static final int BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE = 20;
|
||||||
private static final double ESTABLISHED_MEMORY_CV_THRESHOLD = 0.1;
|
private static final double ESTABLISHED_MEMORY_CV_THRESHOLD = 0.1;
|
||||||
|
|
||||||
private final Client client;
|
private final Client client;
|
||||||
|
@ -866,11 +867,16 @@ public class JobProvider {
|
||||||
* <code>BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE</code> buckets, which is defined as having a coefficient of variation
|
* <code>BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE</code> buckets, which is defined as having a coefficient of variation
|
||||||
* of no more than <code>ESTABLISHED_MEMORY_CV_THRESHOLD</code>
|
* of no more than <code>ESTABLISHED_MEMORY_CV_THRESHOLD</code>
|
||||||
* @param jobId the id of the job for which established memory usage is required
|
* @param jobId the id of the job for which established memory usage is required
|
||||||
|
* @param latestBucketTimestamp the latest bucket timestamp to be used for the calculation, if known, otherwise
|
||||||
|
* <code>null</code>, implying the latest bucket that exists in the results index
|
||||||
|
* @param latestModelSizeStats the latest model size stats for the job, if known, otherwise <code>null</code> - supplying
|
||||||
|
* these when available avoids one search
|
||||||
* @param handler if the method succeeds, this will be passed the established memory usage (in bytes) of the
|
* @param handler if the method succeeds, this will be passed the established memory usage (in bytes) of the
|
||||||
* specified job, or <code>null</code> if memory usage is not yet established
|
* specified job, or 0 if memory usage is not yet established
|
||||||
* @param errorHandler if a problem occurs, the exception will be passed to this handler
|
* @param errorHandler if a problem occurs, the exception will be passed to this handler
|
||||||
*/
|
*/
|
||||||
public void getEstablishedMemoryUsage(String jobId, Consumer<Long> handler, Consumer<Exception> errorHandler) {
|
public void getEstablishedMemoryUsage(String jobId, Date latestBucketTimestamp, ModelSizeStats latestModelSizeStats,
|
||||||
|
Consumer<Long> handler, Consumer<Exception> errorHandler) {
|
||||||
|
|
||||||
String indexName = AnomalyDetectorsIndex.jobResultsAliasedName(jobId);
|
String indexName = AnomalyDetectorsIndex.jobResultsAliasedName(jobId);
|
||||||
|
|
||||||
|
@ -894,7 +900,7 @@ public class JobProvider {
|
||||||
long count = extendedStats.getCount();
|
long count = extendedStats.getCount();
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
// model size stats haven't changed in the last N buckets, so the latest (older) ones are established
|
// model size stats haven't changed in the last N buckets, so the latest (older) ones are established
|
||||||
modelSizeStats(jobId, modelSizeStats -> handleModelBytesOrNull(handler, modelSizeStats), errorHandler);
|
handleLatestModelSizeStats(jobId, latestModelSizeStats, handler, errorHandler);
|
||||||
} else if (count == 1) {
|
} else if (count == 1) {
|
||||||
// no need to do an extra search in the case of exactly one document being aggregated
|
// no need to do an extra search in the case of exactly one document being aggregated
|
||||||
handler.accept((long) extendedStats.getAvg());
|
handler.accept((long) extendedStats.getAvg());
|
||||||
|
@ -905,45 +911,46 @@ public class JobProvider {
|
||||||
// is there sufficient stability in the latest model size stats readings?
|
// is there sufficient stability in the latest model size stats readings?
|
||||||
if (coefficientOfVaration <= ESTABLISHED_MEMORY_CV_THRESHOLD) {
|
if (coefficientOfVaration <= ESTABLISHED_MEMORY_CV_THRESHOLD) {
|
||||||
// yes, so return the latest model size as established
|
// yes, so return the latest model size as established
|
||||||
modelSizeStats(jobId, modelSizeStats -> handleModelBytesOrNull(handler, modelSizeStats),
|
handleLatestModelSizeStats(jobId, latestModelSizeStats, handler, errorHandler);
|
||||||
errorHandler);
|
|
||||||
} else {
|
} else {
|
||||||
// no - we don't have an established model size
|
// no - we don't have an established model size
|
||||||
handler.accept(null);
|
handler.accept(0L);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
handler.accept(null);
|
handler.accept(0L);
|
||||||
}
|
}
|
||||||
}, errorHandler
|
}, errorHandler
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
handler.accept(null);
|
LOGGER.trace("[{}] Insufficient history to calculate established memory use", jobId);
|
||||||
|
handler.accept(0L);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 1. Find the time span of the most recent N bucket results, where N is the number of buckets
|
// Step 1. Find the time span of the most recent N bucket results, where N is the number of buckets
|
||||||
// required to consider memory usage "established"
|
// required to consider memory usage "established"
|
||||||
BucketsQueryBuilder bucketQuery = new BucketsQueryBuilder()
|
BucketsQueryBuilder bucketQuery = new BucketsQueryBuilder()
|
||||||
|
.end(latestBucketTimestamp != null ? Long.toString(latestBucketTimestamp.getTime() + 1) : null)
|
||||||
.sortField(Result.TIMESTAMP.getPreferredName())
|
.sortField(Result.TIMESTAMP.getPreferredName())
|
||||||
.sortDescending(true).from(BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE - 1).size(1)
|
.sortDescending(true).from(BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE - 1).size(1)
|
||||||
.includeInterim(false);
|
.includeInterim(false);
|
||||||
bucketsViaInternalClient(jobId, bucketQuery, bucketHandler, e -> {
|
bucketsViaInternalClient(jobId, bucketQuery, bucketHandler, e -> {
|
||||||
if (e instanceof ResourceNotFoundException) {
|
if (e instanceof ResourceNotFoundException) {
|
||||||
handler.accept(null);
|
handler.accept(0L);
|
||||||
} else {
|
} else {
|
||||||
errorHandler.accept(e);
|
errorHandler.accept(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void handleLatestModelSizeStats(String jobId, ModelSizeStats latestModelSizeStats, Consumer<Long> handler,
|
||||||
* A model size of 0 implies a completely uninitialised model. This method converts 0 to <code>null</code>
|
Consumer<Exception> errorHandler) {
|
||||||
* before calling a handler.
|
if (latestModelSizeStats != null) {
|
||||||
*/
|
handler.accept(latestModelSizeStats.getModelBytes());
|
||||||
private static void handleModelBytesOrNull(Consumer<Long> handler, ModelSizeStats modelSizeStats) {
|
} else {
|
||||||
long modelBytes = modelSizeStats.getModelBytes();
|
modelSizeStats(jobId, modelSizeStats -> handler.accept(modelSizeStats.getModelBytes()), errorHandler);
|
||||||
handler.accept(modelBytes > 0 ? modelBytes : null);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -234,8 +234,9 @@ public class JobResultsPersister extends AbstractComponent {
|
||||||
/**
|
/**
|
||||||
* Persist a model snapshot description
|
* Persist a model snapshot description
|
||||||
*/
|
*/
|
||||||
public void persistModelSnapshot(ModelSnapshot modelSnapshot) {
|
public void persistModelSnapshot(ModelSnapshot modelSnapshot, WriteRequest.RefreshPolicy refreshPolicy) {
|
||||||
Persistable persistable = new Persistable(modelSnapshot.getJobId(), modelSnapshot, ModelSnapshot.documentId(modelSnapshot));
|
Persistable persistable = new Persistable(modelSnapshot.getJobId(), modelSnapshot, ModelSnapshot.documentId(modelSnapshot));
|
||||||
|
persistable.setRefreshPolicy(refreshPolicy);
|
||||||
persistable.persist(AnomalyDetectorsIndex.resultsWriteAlias(modelSnapshot.getJobId())).actionGet();
|
persistable.persist(AnomalyDetectorsIndex.resultsWriteAlias(modelSnapshot.getJobId())).actionGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,8 +248,6 @@ public class JobResultsPersister extends AbstractComponent {
|
||||||
logger.trace("[{}] Persisting model size stats, for size {}", jobId, modelSizeStats.getModelBytes());
|
logger.trace("[{}] Persisting model size stats, for size {}", jobId, modelSizeStats.getModelBytes());
|
||||||
Persistable persistable = new Persistable(jobId, modelSizeStats, modelSizeStats.getId());
|
Persistable persistable = new Persistable(jobId, modelSizeStats, modelSizeStats.getId());
|
||||||
persistable.persist(AnomalyDetectorsIndex.resultsWriteAlias(jobId)).actionGet();
|
persistable.persist(AnomalyDetectorsIndex.resultsWriteAlias(jobId)).actionGet();
|
||||||
// Don't commit as we expect masses of these updates and they're only
|
|
||||||
// for information at the API level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -261,8 +260,6 @@ public class JobResultsPersister extends AbstractComponent {
|
||||||
Persistable persistable = new Persistable(jobId, modelSizeStats, modelSizeStats.getId());
|
Persistable persistable = new Persistable(jobId, modelSizeStats, modelSizeStats.getId());
|
||||||
persistable.setRefreshPolicy(refreshPolicy);
|
persistable.setRefreshPolicy(refreshPolicy);
|
||||||
persistable.persist(AnomalyDetectorsIndex.resultsWriteAlias(jobId), listener);
|
persistable.persist(AnomalyDetectorsIndex.resultsWriteAlias(jobId), listener);
|
||||||
// Don't commit as we expect masses of these updates and they're only
|
|
||||||
// for information at the API level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -381,7 +381,8 @@ public class AutodetectProcessManager extends AbstractComponent {
|
||||||
autodetectParams.quantiles(), autodetectParams.filters(), autoDetectExecutorService,
|
autodetectParams.quantiles(), autodetectParams.filters(), autoDetectExecutorService,
|
||||||
() -> setJobState(jobTask, JobState.FAILED));
|
() -> setJobState(jobTask, JobState.FAILED));
|
||||||
AutoDetectResultProcessor processor = new AutoDetectResultProcessor(
|
AutoDetectResultProcessor processor = new AutoDetectResultProcessor(
|
||||||
client, jobId, renormalizer, jobResultsPersister, autodetectParams.modelSizeStats());
|
client, jobId, renormalizer, jobResultsPersister, jobProvider, autodetectParams.modelSizeStats(),
|
||||||
|
autodetectParams.modelSnapshot() != null);
|
||||||
ExecutorService autodetectWorkerExecutor;
|
ExecutorService autodetectWorkerExecutor;
|
||||||
try {
|
try {
|
||||||
autodetectWorkerExecutor = createAutodetectExecutorService(autoDetectExecutorService);
|
autodetectWorkerExecutor = createAutodetectExecutorService(autoDetectExecutorService);
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.ml.job.process.autodetect.output;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
|
import org.elasticsearch.action.support.WriteRequest;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.logging.Loggers;
|
import org.elasticsearch.common.logging.Loggers;
|
||||||
|
@ -15,6 +16,7 @@ import org.elasticsearch.xpack.ml.MachineLearning;
|
||||||
import org.elasticsearch.xpack.ml.action.PutJobAction;
|
import org.elasticsearch.xpack.ml.action.PutJobAction;
|
||||||
import org.elasticsearch.xpack.ml.action.UpdateJobAction;
|
import org.elasticsearch.xpack.ml.action.UpdateJobAction;
|
||||||
import org.elasticsearch.xpack.ml.job.config.JobUpdate;
|
import org.elasticsearch.xpack.ml.job.config.JobUpdate;
|
||||||
|
import org.elasticsearch.xpack.ml.job.persistence.JobProvider;
|
||||||
import org.elasticsearch.xpack.ml.job.persistence.JobResultsPersister;
|
import org.elasticsearch.xpack.ml.job.persistence.JobResultsPersister;
|
||||||
import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcess;
|
import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcess;
|
||||||
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
||||||
|
@ -31,6 +33,7 @@ import org.elasticsearch.xpack.ml.job.results.Influencer;
|
||||||
import org.elasticsearch.xpack.ml.job.results.ModelPlot;
|
import org.elasticsearch.xpack.ml.job.results.ModelPlot;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -64,31 +67,39 @@ public class AutoDetectResultProcessor {
|
||||||
private final String jobId;
|
private final String jobId;
|
||||||
private final Renormalizer renormalizer;
|
private final Renormalizer renormalizer;
|
||||||
private final JobResultsPersister persister;
|
private final JobResultsPersister persister;
|
||||||
|
private final JobProvider jobProvider;
|
||||||
|
private final boolean restoredSnapshot;
|
||||||
|
|
||||||
final CountDownLatch completionLatch = new CountDownLatch(1);
|
final CountDownLatch completionLatch = new CountDownLatch(1);
|
||||||
final Semaphore updateModelSnapshotIdSemaphore = new Semaphore(1);
|
final Semaphore updateModelSnapshotIdSemaphore = new Semaphore(1);
|
||||||
private final FlushListener flushListener;
|
private final FlushListener flushListener;
|
||||||
private volatile boolean processKilled;
|
private volatile boolean processKilled;
|
||||||
private volatile boolean failed;
|
private volatile boolean failed;
|
||||||
|
private int bucketCount; // only used from the process() thread, so doesn't need to be volatile
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New model size stats are read as the process is running
|
* New model size stats are read as the process is running
|
||||||
*/
|
*/
|
||||||
private volatile ModelSizeStats latestModelSizeStats;
|
private volatile ModelSizeStats latestModelSizeStats;
|
||||||
|
private volatile long latestEstablishedModelMemory;
|
||||||
|
private volatile boolean haveNewLatestModelSizeStats;
|
||||||
|
|
||||||
public AutoDetectResultProcessor(Client client, String jobId, Renormalizer renormalizer, JobResultsPersister persister,
|
public AutoDetectResultProcessor(Client client, String jobId, Renormalizer renormalizer, JobResultsPersister persister,
|
||||||
ModelSizeStats latestModelSizeStats) {
|
JobProvider jobProvider, ModelSizeStats latestModelSizeStats, boolean restoredSnapshot) {
|
||||||
this(client, jobId, renormalizer, persister, latestModelSizeStats, new FlushListener());
|
this(client, jobId, renormalizer, persister, jobProvider, latestModelSizeStats, restoredSnapshot, new FlushListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoDetectResultProcessor(Client client, String jobId, Renormalizer renormalizer, JobResultsPersister persister,
|
AutoDetectResultProcessor(Client client, String jobId, Renormalizer renormalizer, JobResultsPersister persister,
|
||||||
ModelSizeStats latestModelSizeStats, FlushListener flushListener) {
|
JobProvider jobProvider, ModelSizeStats latestModelSizeStats, boolean restoredSnapshot,
|
||||||
|
FlushListener flushListener) {
|
||||||
this.client = Objects.requireNonNull(client);
|
this.client = Objects.requireNonNull(client);
|
||||||
this.jobId = Objects.requireNonNull(jobId);
|
this.jobId = Objects.requireNonNull(jobId);
|
||||||
this.renormalizer = Objects.requireNonNull(renormalizer);
|
this.renormalizer = Objects.requireNonNull(renormalizer);
|
||||||
this.persister = Objects.requireNonNull(persister);
|
this.persister = Objects.requireNonNull(persister);
|
||||||
|
this.jobProvider = Objects.requireNonNull(jobProvider);
|
||||||
this.flushListener = Objects.requireNonNull(flushListener);
|
this.flushListener = Objects.requireNonNull(flushListener);
|
||||||
this.latestModelSizeStats = Objects.requireNonNull(latestModelSizeStats);
|
this.latestModelSizeStats = Objects.requireNonNull(latestModelSizeStats);
|
||||||
|
this.restoredSnapshot = restoredSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(AutodetectProcess process) {
|
public void process(AutodetectProcess process) {
|
||||||
|
@ -98,14 +109,13 @@ public class AutoDetectResultProcessor {
|
||||||
// to kill the results reader thread as autodetect will be blocked
|
// to kill the results reader thread as autodetect will be blocked
|
||||||
// trying to write its output.
|
// trying to write its output.
|
||||||
try {
|
try {
|
||||||
int bucketCount = 0;
|
bucketCount = 0;
|
||||||
Iterator<AutodetectResult> iterator = process.readAutodetectResults();
|
Iterator<AutodetectResult> iterator = process.readAutodetectResults();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
try {
|
try {
|
||||||
AutodetectResult result = iterator.next();
|
AutodetectResult result = iterator.next();
|
||||||
processResult(context, result);
|
processResult(context, result);
|
||||||
if (result.getBucket() != null) {
|
if (result.getBucket() != null) {
|
||||||
bucketCount++;
|
|
||||||
LOGGER.trace("[{}] Bucket number {} parsed from output", jobId, bucketCount);
|
LOGGER.trace("[{}] Bucket number {} parsed from output", jobId, bucketCount);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -174,6 +184,17 @@ public class AutoDetectResultProcessor {
|
||||||
// persist after deleting interim results in case the new
|
// persist after deleting interim results in case the new
|
||||||
// results are also interim
|
// results are also interim
|
||||||
context.bulkResultsPersister.persistBucket(bucket).executeRequest();
|
context.bulkResultsPersister.persistBucket(bucket).executeRequest();
|
||||||
|
++bucketCount;
|
||||||
|
|
||||||
|
// if we haven't previously set established model memory, consider trying again after
|
||||||
|
// a reasonable amount of time has elapsed since the last model size stats update
|
||||||
|
long minEstablishedTimespanMs = JobProvider.BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE * bucket.getBucketSpan() * 1000L;
|
||||||
|
if (haveNewLatestModelSizeStats && latestEstablishedModelMemory == 0
|
||||||
|
&& bucket.getTimestamp().getTime() > latestModelSizeStats.getTimestamp().getTime() + minEstablishedTimespanMs) {
|
||||||
|
persister.commitResultWrites(context.jobId);
|
||||||
|
updateEstablishedModelMemoryOnJob(bucket.getTimestamp(), latestModelSizeStats);
|
||||||
|
haveNewLatestModelSizeStats = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
List<AnomalyRecord> records = result.getRecords();
|
List<AnomalyRecord> records = result.getRecords();
|
||||||
if (records != null && !records.isEmpty()) {
|
if (records != null && !records.isEmpty()) {
|
||||||
|
@ -218,14 +239,21 @@ public class AutoDetectResultProcessor {
|
||||||
modelSizeStats.getBucketAllocationFailuresCount(), modelSizeStats.getMemoryStatus());
|
modelSizeStats.getBucketAllocationFailuresCount(), modelSizeStats.getMemoryStatus());
|
||||||
|
|
||||||
latestModelSizeStats = modelSizeStats;
|
latestModelSizeStats = modelSizeStats;
|
||||||
|
haveNewLatestModelSizeStats = true;
|
||||||
persister.persistModelSizeStats(modelSizeStats);
|
persister.persistModelSizeStats(modelSizeStats);
|
||||||
|
// This is a crude way to NOT refresh the index and NOT attempt to update established model memory during the first 20 buckets
|
||||||
|
// because this is when the model size stats are likely to be least stable and lots of updates will be coming through, and
|
||||||
|
// we'll NEVER consider memory usage to be established during this period
|
||||||
|
if (restoredSnapshot || bucketCount >= JobProvider.BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE) {
|
||||||
|
// We need to make all results written up to and including these stats available for the established memory calculation
|
||||||
|
persister.commitResultWrites(context.jobId);
|
||||||
|
updateEstablishedModelMemoryOnJob(modelSizeStats.getTimestamp(), modelSizeStats);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ModelSnapshot modelSnapshot = result.getModelSnapshot();
|
ModelSnapshot modelSnapshot = result.getModelSnapshot();
|
||||||
if (modelSnapshot != null) {
|
if (modelSnapshot != null) {
|
||||||
persister.persistModelSnapshot(modelSnapshot);
|
// We need to refresh in order for the snapshot to be available when we try to update the job with it
|
||||||
// We need to refresh the index in order for the snapshot to be available when we'll try to
|
persister.persistModelSnapshot(modelSnapshot, WriteRequest.RefreshPolicy.IMMEDIATE);
|
||||||
// update the job with it
|
|
||||||
persister.commitResultWrites(jobId);
|
|
||||||
updateModelSnapshotIdOnJob(modelSnapshot);
|
updateModelSnapshotIdOnJob(modelSnapshot);
|
||||||
}
|
}
|
||||||
Quantiles quantiles = result.getQuantiles();
|
Quantiles quantiles = result.getQuantiles();
|
||||||
|
@ -286,6 +314,28 @@ public class AutoDetectResultProcessor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateEstablishedModelMemoryOnJob(Date latestBucketTimestamp, ModelSizeStats modelSizeStats) {
|
||||||
|
jobProvider.getEstablishedMemoryUsage(jobId, latestBucketTimestamp, modelSizeStats, establishedModelMemory -> {
|
||||||
|
JobUpdate update = new JobUpdate.Builder(jobId)
|
||||||
|
.setEstablishedModelMemory(establishedModelMemory).build();
|
||||||
|
UpdateJobAction.Request updateRequest = new UpdateJobAction.Request(jobId, update);
|
||||||
|
|
||||||
|
client.execute(UpdateJobAction.INSTANCE, updateRequest, new ActionListener<PutJobAction.Response>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(PutJobAction.Response response) {
|
||||||
|
latestEstablishedModelMemory = establishedModelMemory;
|
||||||
|
LOGGER.debug("[{}] Updated job with established model memory [{}]", jobId, establishedModelMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Exception e) {
|
||||||
|
LOGGER.error("[" + jobId + "] Failed to update job with new established model memory [" + establishedModelMemory + "]",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, e -> LOGGER.error("[" + jobId + "] Failed to calculate established model memory", e));
|
||||||
|
}
|
||||||
|
|
||||||
public void awaitCompletion() throws TimeoutException {
|
public void awaitCompletion() throws TimeoutException {
|
||||||
try {
|
try {
|
||||||
// Although the results won't take 30 minutes to finish, the pipe won't be closed
|
// Although the results won't take 30 minutes to finish, the pipe won't be closed
|
||||||
|
|
|
@ -8,11 +8,15 @@ package org.elasticsearch.xpack.ml;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
|
import org.elasticsearch.monitor.os.OsStats;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class MachineLearningTests extends ESTestCase {
|
public class MachineLearningTests extends ESTestCase {
|
||||||
|
|
||||||
|
@ -67,6 +71,42 @@ public class MachineLearningTests extends ESTestCase {
|
||||||
"it is reserved for machine learning. If your intention was to customize machine learning, set the [xpack.ml."));
|
"it is reserved for machine learning. If your intention was to customize machine learning, set the [xpack.ml."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMachineMemory_givenStatsFailure() throws IOException {
|
||||||
|
OsStats stats = mock(OsStats.class);
|
||||||
|
when(stats.getMem()).thenReturn(new OsStats.Mem(-1, -1));
|
||||||
|
assertEquals(-1L, MachineLearning.machineMemoryFromStats(stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMachineMemory_givenNoCgroup() throws IOException {
|
||||||
|
OsStats stats = mock(OsStats.class);
|
||||||
|
when(stats.getMem()).thenReturn(new OsStats.Mem(10_737_418_240L, 5_368_709_120L));
|
||||||
|
assertEquals(10_737_418_240L, MachineLearning.machineMemoryFromStats(stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMachineMemory_givenCgroupNullLimit() throws IOException {
|
||||||
|
OsStats stats = mock(OsStats.class);
|
||||||
|
when(stats.getMem()).thenReturn(new OsStats.Mem(10_737_418_240L, 5_368_709_120L));
|
||||||
|
when(stats.getCgroup()).thenReturn(new OsStats.Cgroup("a", 1, "b", 2, 3,
|
||||||
|
new OsStats.Cgroup.CpuStat(4, 5, 6), null, null, null));
|
||||||
|
assertEquals(10_737_418_240L, MachineLearning.machineMemoryFromStats(stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMachineMemory_givenCgroupNoLimit() throws IOException {
|
||||||
|
OsStats stats = mock(OsStats.class);
|
||||||
|
when(stats.getMem()).thenReturn(new OsStats.Mem(10_737_418_240L, 5_368_709_120L));
|
||||||
|
when(stats.getCgroup()).thenReturn(new OsStats.Cgroup("a", 1, "b", 2, 3,
|
||||||
|
new OsStats.Cgroup.CpuStat(4, 5, 6), "c", "18446744073709551615", "4796416"));
|
||||||
|
assertEquals(10_737_418_240L, MachineLearning.machineMemoryFromStats(stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMachineMemory_givenCgroupLowLimit() throws IOException {
|
||||||
|
OsStats stats = mock(OsStats.class);
|
||||||
|
when(stats.getMem()).thenReturn(new OsStats.Mem(10_737_418_240L, 5_368_709_120L));
|
||||||
|
when(stats.getCgroup()).thenReturn(new OsStats.Cgroup("a", 1, "b", 2, 3,
|
||||||
|
new OsStats.Cgroup.CpuStat(4, 5, 6), "c", "7516192768", "4796416"));
|
||||||
|
assertEquals(7_516_192_768L, MachineLearning.machineMemoryFromStats(stats));
|
||||||
|
}
|
||||||
|
|
||||||
private MachineLearning createMachineLearning(Settings settings) {
|
private MachineLearning createMachineLearning(Settings settings) {
|
||||||
return new MachineLearning(settings, mock(Environment.class), mock(XPackLicenseState.class));
|
return new MachineLearning(settings, mock(Environment.class), mock(XPackLicenseState.class));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.transport.TransportAddress;
|
import org.elasticsearch.common.transport.TransportAddress;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.rest.RestStatus;
|
import org.elasticsearch.rest.RestStatus;
|
||||||
|
@ -91,9 +93,10 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
OpenJobAction.validate("job_id", mlBuilder.build());
|
OpenJobAction.validate("job_id", mlBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSelectLeastLoadedMlNode() {
|
public void testSelectLeastLoadedMlNode_byCount() {
|
||||||
Map<String, String> nodeAttr = new HashMap<>();
|
Map<String, String> nodeAttr = new HashMap<>();
|
||||||
nodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true");
|
nodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true");
|
||||||
|
// MachineLearning.MACHINE_MEMORY_NODE_ATTR not set, so this will fall back to allocating by count
|
||||||
DiscoveryNodes nodes = DiscoveryNodes.builder()
|
DiscoveryNodes nodes = DiscoveryNodes.builder()
|
||||||
.add(new DiscoveryNode("_node_name1", "_node_id1", new TransportAddress(InetAddress.getLoopbackAddress(), 9300),
|
.add(new DiscoveryNode("_node_name1", "_node_id1", new TransportAddress(InetAddress.getLoopbackAddress(), 9300),
|
||||||
nodeAttr, Collections.emptySet(), Version.CURRENT))
|
nodeAttr, Collections.emptySet(), Version.CURRENT))
|
||||||
|
@ -117,10 +120,49 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
||||||
cs.metaData(metaData);
|
cs.metaData(metaData);
|
||||||
cs.routingTable(routingTable.build());
|
cs.routingTable(routingTable.build());
|
||||||
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id4", cs.build(), 2, 10, logger);
|
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id4", cs.build(), 2, 10, 30, logger);
|
||||||
|
assertEquals("", result.getExplanation());
|
||||||
assertEquals("_node_id3", result.getExecutorNode());
|
assertEquals("_node_id3", result.getExecutorNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSelectLeastLoadedMlNode_byMemory() {
|
||||||
|
Map<String, String> nodeAttr = new HashMap<>();
|
||||||
|
nodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true");
|
||||||
|
nodeAttr.put(MachineLearning.MACHINE_MEMORY_NODE_ATTR, "16000000000");
|
||||||
|
DiscoveryNodes nodes = DiscoveryNodes.builder()
|
||||||
|
.add(new DiscoveryNode("_node_name1", "_node_id1", new TransportAddress(InetAddress.getLoopbackAddress(), 9300),
|
||||||
|
nodeAttr, Collections.emptySet(), Version.CURRENT))
|
||||||
|
.add(new DiscoveryNode("_node_name2", "_node_id2", new TransportAddress(InetAddress.getLoopbackAddress(), 9301),
|
||||||
|
nodeAttr, Collections.emptySet(), Version.CURRENT))
|
||||||
|
.add(new DiscoveryNode("_node_name3", "_node_id3", new TransportAddress(InetAddress.getLoopbackAddress(), 9302),
|
||||||
|
nodeAttr, Collections.emptySet(), Version.CURRENT))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder();
|
||||||
|
addJobTask("job_id1", "_node_id1", JobState.fromString("opened"), tasksBuilder);
|
||||||
|
addJobTask("job_id2", "_node_id2", JobState.fromString("opened"), tasksBuilder);
|
||||||
|
addJobTask("job_id3", "_node_id2", JobState.fromString("opened"), tasksBuilder);
|
||||||
|
addJobTask("job_id4", "_node_id3", JobState.fromString("opened"), tasksBuilder);
|
||||||
|
PersistentTasksCustomMetaData tasks = tasksBuilder.build();
|
||||||
|
|
||||||
|
ClusterState.Builder cs = ClusterState.builder(new ClusterName("_name"));
|
||||||
|
MetaData.Builder metaData = MetaData.builder();
|
||||||
|
RoutingTable.Builder routingTable = RoutingTable.builder();
|
||||||
|
addJobAndIndices(metaData, routingTable, jobId -> {
|
||||||
|
// remember we add 100MB for the process overhead, so these model memory
|
||||||
|
// limits correspond to estimated footprints of 102MB and 205MB
|
||||||
|
long jobSize = (jobId.equals("job_id2") || jobId.equals("job_id3")) ? 2 : 105;
|
||||||
|
return BaseMlIntegTestCase.createFareQuoteJob(jobId, new ByteSizeValue(jobSize, ByteSizeUnit.MB)).build(new Date());
|
||||||
|
}, "job_id1", "job_id2", "job_id3", "job_id4", "job_id5");
|
||||||
|
cs.nodes(nodes);
|
||||||
|
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
||||||
|
cs.metaData(metaData);
|
||||||
|
cs.routingTable(routingTable.build());
|
||||||
|
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id5", cs.build(), 2, 10, 30, logger);
|
||||||
|
assertEquals("", result.getExplanation());
|
||||||
|
assertEquals("_node_id2", result.getExecutorNode());
|
||||||
|
}
|
||||||
|
|
||||||
public void testSelectLeastLoadedMlNode_maxCapacity() {
|
public void testSelectLeastLoadedMlNode_maxCapacity() {
|
||||||
int numNodes = randomIntBetween(1, 10);
|
int numNodes = randomIntBetween(1, 10);
|
||||||
int maxRunningJobsPerNode = randomIntBetween(1, 100);
|
int maxRunningJobsPerNode = randomIntBetween(1, 100);
|
||||||
|
@ -129,13 +171,15 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
nodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true");
|
nodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true");
|
||||||
DiscoveryNodes.Builder nodes = DiscoveryNodes.builder();
|
DiscoveryNodes.Builder nodes = DiscoveryNodes.builder();
|
||||||
PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder();
|
PersistentTasksCustomMetaData.Builder tasksBuilder = PersistentTasksCustomMetaData.builder();
|
||||||
|
String[] jobIds = new String[numNodes * maxRunningJobsPerNode];
|
||||||
for (int i = 0; i < numNodes; i++) {
|
for (int i = 0; i < numNodes; i++) {
|
||||||
String nodeId = "_node_id" + i;
|
String nodeId = "_node_id" + i;
|
||||||
TransportAddress address = new TransportAddress(InetAddress.getLoopbackAddress(), 9300 + i);
|
TransportAddress address = new TransportAddress(InetAddress.getLoopbackAddress(), 9300 + i);
|
||||||
nodes.add(new DiscoveryNode("_node_name" + i, nodeId, address, nodeAttr, Collections.emptySet(), Version.CURRENT));
|
nodes.add(new DiscoveryNode("_node_name" + i, nodeId, address, nodeAttr, Collections.emptySet(), Version.CURRENT));
|
||||||
for (int j = 0; j < maxRunningJobsPerNode; j++) {
|
for (int j = 0; j < maxRunningJobsPerNode; j++) {
|
||||||
long id = j + (maxRunningJobsPerNode * i);
|
int id = j + (maxRunningJobsPerNode * i);
|
||||||
addJobTask("job_id" + id, nodeId, JobState.OPENED, tasksBuilder);
|
jobIds[id] = "job_id" + id;
|
||||||
|
addJobTask(jobIds[id], nodeId, JobState.OPENED, tasksBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PersistentTasksCustomMetaData tasks = tasksBuilder.build();
|
PersistentTasksCustomMetaData tasks = tasksBuilder.build();
|
||||||
|
@ -143,12 +187,12 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
ClusterState.Builder cs = ClusterState.builder(new ClusterName("_name"));
|
ClusterState.Builder cs = ClusterState.builder(new ClusterName("_name"));
|
||||||
MetaData.Builder metaData = MetaData.builder();
|
MetaData.Builder metaData = MetaData.builder();
|
||||||
RoutingTable.Builder routingTable = RoutingTable.builder();
|
RoutingTable.Builder routingTable = RoutingTable.builder();
|
||||||
addJobAndIndices(metaData, routingTable, "job_id1", "job_id2");
|
addJobAndIndices(metaData, routingTable, jobIds);
|
||||||
cs.nodes(nodes);
|
cs.nodes(nodes);
|
||||||
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
||||||
cs.metaData(metaData);
|
cs.metaData(metaData);
|
||||||
cs.routingTable(routingTable.build());
|
cs.routingTable(routingTable.build());
|
||||||
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id2", cs.build(), 2, maxRunningJobsPerNode, logger);
|
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id2", cs.build(), 2, maxRunningJobsPerNode, 30, logger);
|
||||||
assertNull(result.getExecutorNode());
|
assertNull(result.getExecutorNode());
|
||||||
assertTrue(result.getExplanation().contains("because this node is full. Number of opened jobs [" + maxRunningJobsPerNode
|
assertTrue(result.getExplanation().contains("because this node is full. Number of opened jobs [" + maxRunningJobsPerNode
|
||||||
+ "], xpack.ml.max_open_jobs [" + maxRunningJobsPerNode + "]"));
|
+ "], xpack.ml.max_open_jobs [" + maxRunningJobsPerNode + "]"));
|
||||||
|
@ -174,7 +218,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
||||||
cs.metaData(metaData);
|
cs.metaData(metaData);
|
||||||
cs.routingTable(routingTable.build());
|
cs.routingTable(routingTable.build());
|
||||||
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id2", cs.build(), 2, 10, logger);
|
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id2", cs.build(), 2, 10, 30, logger);
|
||||||
assertTrue(result.getExplanation().contains("because this node isn't a ml node"));
|
assertTrue(result.getExplanation().contains("because this node isn't a ml node"));
|
||||||
assertNull(result.getExecutorNode());
|
assertNull(result.getExecutorNode());
|
||||||
}
|
}
|
||||||
|
@ -209,7 +253,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
csBuilder.metaData(metaData);
|
csBuilder.metaData(metaData);
|
||||||
|
|
||||||
ClusterState cs = csBuilder.build();
|
ClusterState cs = csBuilder.build();
|
||||||
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id6", cs, 2, 10, logger);
|
Assignment result = OpenJobAction.selectLeastLoadedMlNode("job_id6", cs, 2, 10, 30, logger);
|
||||||
assertEquals("_node_id3", result.getExecutorNode());
|
assertEquals("_node_id3", result.getExecutorNode());
|
||||||
|
|
||||||
tasksBuilder = PersistentTasksCustomMetaData.builder(tasks);
|
tasksBuilder = PersistentTasksCustomMetaData.builder(tasks);
|
||||||
|
@ -219,7 +263,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
csBuilder = ClusterState.builder(cs);
|
csBuilder = ClusterState.builder(cs);
|
||||||
csBuilder.metaData(MetaData.builder(cs.metaData()).putCustom(PersistentTasksCustomMetaData.TYPE, tasks));
|
csBuilder.metaData(MetaData.builder(cs.metaData()).putCustom(PersistentTasksCustomMetaData.TYPE, tasks));
|
||||||
cs = csBuilder.build();
|
cs = csBuilder.build();
|
||||||
result = OpenJobAction.selectLeastLoadedMlNode("job_id7", cs, 2, 10, logger);
|
result = OpenJobAction.selectLeastLoadedMlNode("job_id7", cs, 2, 10, 30, logger);
|
||||||
assertNull("no node selected, because OPENING state", result.getExecutorNode());
|
assertNull("no node selected, because OPENING state", result.getExecutorNode());
|
||||||
assertTrue(result.getExplanation().contains("because node exceeds [2] the maximum number of jobs [2] in opening state"));
|
assertTrue(result.getExplanation().contains("because node exceeds [2] the maximum number of jobs [2] in opening state"));
|
||||||
|
|
||||||
|
@ -230,7 +274,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
csBuilder = ClusterState.builder(cs);
|
csBuilder = ClusterState.builder(cs);
|
||||||
csBuilder.metaData(MetaData.builder(cs.metaData()).putCustom(PersistentTasksCustomMetaData.TYPE, tasks));
|
csBuilder.metaData(MetaData.builder(cs.metaData()).putCustom(PersistentTasksCustomMetaData.TYPE, tasks));
|
||||||
cs = csBuilder.build();
|
cs = csBuilder.build();
|
||||||
result = OpenJobAction.selectLeastLoadedMlNode("job_id7", cs, 2, 10, logger);
|
result = OpenJobAction.selectLeastLoadedMlNode("job_id7", cs, 2, 10, 30, logger);
|
||||||
assertNull("no node selected, because stale task", result.getExecutorNode());
|
assertNull("no node selected, because stale task", result.getExecutorNode());
|
||||||
assertTrue(result.getExplanation().contains("because node exceeds [2] the maximum number of jobs [2] in opening state"));
|
assertTrue(result.getExplanation().contains("because node exceeds [2] the maximum number of jobs [2] in opening state"));
|
||||||
|
|
||||||
|
@ -241,7 +285,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
csBuilder = ClusterState.builder(cs);
|
csBuilder = ClusterState.builder(cs);
|
||||||
csBuilder.metaData(MetaData.builder(cs.metaData()).putCustom(PersistentTasksCustomMetaData.TYPE, tasks));
|
csBuilder.metaData(MetaData.builder(cs.metaData()).putCustom(PersistentTasksCustomMetaData.TYPE, tasks));
|
||||||
cs = csBuilder.build();
|
cs = csBuilder.build();
|
||||||
result = OpenJobAction.selectLeastLoadedMlNode("job_id7", cs, 2, 10, logger);
|
result = OpenJobAction.selectLeastLoadedMlNode("job_id7", cs, 2, 10, 30, logger);
|
||||||
assertNull("no node selected, because null state", result.getExecutorNode());
|
assertNull("no node selected, because null state", result.getExecutorNode());
|
||||||
assertTrue(result.getExplanation().contains("because node exceeds [2] the maximum number of jobs [2] in opening state"));
|
assertTrue(result.getExplanation().contains("because node exceeds [2] the maximum number of jobs [2] in opening state"));
|
||||||
}
|
}
|
||||||
|
@ -276,7 +320,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
||||||
cs.metaData(metaData);
|
cs.metaData(metaData);
|
||||||
cs.routingTable(routingTable.build());
|
cs.routingTable(routingTable.build());
|
||||||
Assignment result = OpenJobAction.selectLeastLoadedMlNode("incompatible_type_job", cs.build(), 2, 10, logger);
|
Assignment result = OpenJobAction.selectLeastLoadedMlNode("incompatible_type_job", cs.build(), 2, 10, 30, logger);
|
||||||
assertThat(result.getExplanation(), containsString("because this node does not support jobs of type [incompatible_type]"));
|
assertThat(result.getExplanation(), containsString("because this node does not support jobs of type [incompatible_type]"));
|
||||||
assertNull(result.getExecutorNode());
|
assertNull(result.getExecutorNode());
|
||||||
}
|
}
|
||||||
|
@ -303,7 +347,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
metaData.putCustom(PersistentTasksCustomMetaData.TYPE, tasks);
|
||||||
cs.metaData(metaData);
|
cs.metaData(metaData);
|
||||||
cs.routingTable(routingTable.build());
|
cs.routingTable(routingTable.build());
|
||||||
Assignment result = OpenJobAction.selectLeastLoadedMlNode("incompatible_type_job", cs.build(), 2, 10, logger);
|
Assignment result = OpenJobAction.selectLeastLoadedMlNode("incompatible_type_job", cs.build(), 2, 10, 30, logger);
|
||||||
assertThat(result.getExplanation(), containsString("because this node does not support jobs of version [" + Version.CURRENT + "]"));
|
assertThat(result.getExplanation(), containsString("because this node does not support jobs of version [" + Version.CURRENT + "]"));
|
||||||
assertNull(result.getExecutorNode());
|
assertNull(result.getExecutorNode());
|
||||||
}
|
}
|
||||||
|
@ -402,7 +446,7 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMappingRequiresUpdateSomeVersionMix() throws IOException {
|
public void testMappingRequiresUpdateSomeVersionMix() throws IOException {
|
||||||
Map<String, Object> versionMix = new HashMap<String, Object>();
|
Map<String, Object> versionMix = new HashMap<>();
|
||||||
versionMix.put("version_54", Version.V_5_4_0);
|
versionMix.put("version_54", Version.V_5_4_0);
|
||||||
versionMix.put("version_current", Version.CURRENT);
|
versionMix.put("version_current", Version.CURRENT);
|
||||||
versionMix.put("version_null", null);
|
versionMix.put("version_null", null);
|
||||||
|
@ -425,7 +469,8 @@ public class OpenJobActionTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addJobAndIndices(MetaData.Builder metaData, RoutingTable.Builder routingTable, String... jobIds) {
|
private void addJobAndIndices(MetaData.Builder metaData, RoutingTable.Builder routingTable, String... jobIds) {
|
||||||
addJobAndIndices(metaData, routingTable, jobId -> BaseMlIntegTestCase.createFareQuoteJob(jobId).build(new Date()), jobIds);
|
addJobAndIndices(metaData, routingTable, jobId ->
|
||||||
|
BaseMlIntegTestCase.createFareQuoteJob(jobId, new ByteSizeValue(2, ByteSizeUnit.MB)).build(new Date()), jobIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addJobAndIndices(MetaData.Builder metaData, RoutingTable.Builder routingTable, Function<String, Job> jobCreator,
|
private void addJobAndIndices(MetaData.Builder metaData, RoutingTable.Builder routingTable, Function<String, Job> jobCreator,
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class AutodetectResultProcessorIT extends XPackSingleNodeTestCase {
|
||||||
jobProvider = new JobProvider(client(), builder.build());
|
jobProvider = new JobProvider(client(), builder.build());
|
||||||
capturedUpdateModelSnapshotOnJobRequests = new ArrayList<>();
|
capturedUpdateModelSnapshotOnJobRequests = new ArrayList<>();
|
||||||
resultProcessor = new AutoDetectResultProcessor(client(), JOB_ID, new NoOpRenormalizer(),
|
resultProcessor = new AutoDetectResultProcessor(client(), JOB_ID, new NoOpRenormalizer(),
|
||||||
new JobResultsPersister(nodeSettings(), client()), new ModelSizeStats.Builder(JOB_ID).build()) {
|
new JobResultsPersister(nodeSettings(), client()), jobProvider, new ModelSizeStats.Builder(JOB_ID).build(), false) {
|
||||||
@Override
|
@Override
|
||||||
protected void updateModelSnapshotIdOnJob(ModelSnapshot modelSnapshot) {
|
protected void updateModelSnapshotIdOnJob(ModelSnapshot modelSnapshot) {
|
||||||
capturedUpdateModelSnapshotOnJobRequests.add(modelSnapshot);
|
capturedUpdateModelSnapshotOnJobRequests.add(modelSnapshot);
|
||||||
|
|
|
@ -12,6 +12,8 @@ import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.common.CheckedRunnable;
|
import org.elasticsearch.common.CheckedRunnable;
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
import org.elasticsearch.search.aggregations.AggregationBuilders;
|
||||||
|
@ -44,19 +46,19 @@ import org.elasticsearch.xpack.persistent.PersistentTasksCustomMetaData.Persiste
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.hasEntry;
|
||||||
|
|
||||||
public class BasicDistributedJobsIT extends BaseMlIntegTestCase {
|
public class BasicDistributedJobsIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
public void testFailOverBasics() throws Exception {
|
public void testFailOverBasics() throws Exception {
|
||||||
internalCluster().ensureAtLeastNumDataNodes(4);
|
internalCluster().ensureAtLeastNumDataNodes(4);
|
||||||
ensureStableCluster(4);
|
ensureStableCluster(4);
|
||||||
|
|
||||||
Job.Builder job = createJob("fail-over-basics-job");
|
Job.Builder job = createJob("fail-over-basics-job", new ByteSizeValue(2, ByteSizeUnit.MB));
|
||||||
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
||||||
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
||||||
assertTrue(putJobResponse.isAcknowledged());
|
assertTrue(putJobResponse.isAcknowledged());
|
||||||
|
@ -200,7 +202,7 @@ public class BasicDistributedJobsIT extends BaseMlIntegTestCase {
|
||||||
ensureStableCluster(3);
|
ensureStableCluster(3);
|
||||||
|
|
||||||
String jobId = "dedicated-ml-node-job";
|
String jobId = "dedicated-ml-node-job";
|
||||||
Job.Builder job = createJob(jobId);
|
Job.Builder job = createJob(jobId, new ByteSizeValue(2, ByteSizeUnit.MB));
|
||||||
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
||||||
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
||||||
assertTrue(putJobResponse.isAcknowledged());
|
assertTrue(putJobResponse.isAcknowledged());
|
||||||
|
@ -213,10 +215,8 @@ public class BasicDistributedJobsIT extends BaseMlIntegTestCase {
|
||||||
PersistentTask task = tasks.getTask(MlMetadata.jobTaskId(jobId));
|
PersistentTask task = tasks.getTask(MlMetadata.jobTaskId(jobId));
|
||||||
|
|
||||||
DiscoveryNode node = clusterState.nodes().resolveNode(task.getExecutorNode());
|
DiscoveryNode node = clusterState.nodes().resolveNode(task.getExecutorNode());
|
||||||
Map<String, String> expectedNodeAttr = new HashMap<>();
|
assertThat(node.getAttributes(), hasEntry(MachineLearning.ML_ENABLED_NODE_ATTR, "true"));
|
||||||
expectedNodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true");
|
assertThat(node.getAttributes(), hasEntry(MachineLearning.MAX_OPEN_JOBS_NODE_ATTR, "10"));
|
||||||
expectedNodeAttr.put(MachineLearning.MAX_OPEN_JOBS_NODE_ATTR, "10");
|
|
||||||
assertEquals(expectedNodeAttr, node.getAttributes());
|
|
||||||
JobTaskStatus jobTaskStatus = (JobTaskStatus) task.getStatus();
|
JobTaskStatus jobTaskStatus = (JobTaskStatus) task.getStatus();
|
||||||
assertNotNull(jobTaskStatus);
|
assertNotNull(jobTaskStatus);
|
||||||
assertEquals(JobState.OPENED, jobTaskStatus.getState());
|
assertEquals(JobState.OPENED, jobTaskStatus.getState());
|
||||||
|
@ -284,7 +284,7 @@ public class BasicDistributedJobsIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
int numJobs = numMlNodes * 10;
|
int numJobs = numMlNodes * 10;
|
||||||
for (int i = 0; i < numJobs; i++) {
|
for (int i = 0; i < numJobs; i++) {
|
||||||
Job.Builder job = createJob(Integer.toString(i));
|
Job.Builder job = createJob(Integer.toString(i), new ByteSizeValue(2, ByteSizeUnit.MB));
|
||||||
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
||||||
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
||||||
assertTrue(putJobResponse.isAcknowledged());
|
assertTrue(putJobResponse.isAcknowledged());
|
||||||
|
@ -401,10 +401,8 @@ public class BasicDistributedJobsIT extends BaseMlIntegTestCase {
|
||||||
assertNotNull(task.getExecutorNode());
|
assertNotNull(task.getExecutorNode());
|
||||||
assertFalse(task.needsReassignment(clusterState.nodes()));
|
assertFalse(task.needsReassignment(clusterState.nodes()));
|
||||||
DiscoveryNode node = clusterState.nodes().resolveNode(task.getExecutorNode());
|
DiscoveryNode node = clusterState.nodes().resolveNode(task.getExecutorNode());
|
||||||
Map<String, String> expectedNodeAttr = new HashMap<>();
|
assertThat(node.getAttributes(), hasEntry(MachineLearning.ML_ENABLED_NODE_ATTR, "true"));
|
||||||
expectedNodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true");
|
assertThat(node.getAttributes(), hasEntry(MachineLearning.MAX_OPEN_JOBS_NODE_ATTR, "10"));
|
||||||
expectedNodeAttr.put(MachineLearning.MAX_OPEN_JOBS_NODE_ATTR, "10");
|
|
||||||
assertEquals(expectedNodeAttr, node.getAttributes());
|
|
||||||
|
|
||||||
JobTaskStatus jobTaskStatus = (JobTaskStatus) task.getStatus();
|
JobTaskStatus jobTaskStatus = (JobTaskStatus) task.getStatus();
|
||||||
assertNotNull(jobTaskStatus);
|
assertNotNull(jobTaskStatus);
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
package org.elasticsearch.xpack.ml.integration;
|
package org.elasticsearch.xpack.ml.integration;
|
||||||
|
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.xpack.ml.action.GetJobsStatsAction;
|
||||||
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
||||||
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Detector;
|
import org.elasticsearch.xpack.ml.job.config.Detector;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
|
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
||||||
import org.elasticsearch.xpack.ml.job.results.AnomalyRecord;
|
import org.elasticsearch.xpack.ml.job.results.AnomalyRecord;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
|
@ -66,6 +68,13 @@ public class BasicRenormalizationIT extends MlNativeAutodetectIntegTestCase {
|
||||||
// This is the key assertion: if renormalization never happened then the record_score would
|
// This is the key assertion: if renormalization never happened then the record_score would
|
||||||
// be the same as the initial_record_score on the anomaly record that happened earlier
|
// be the same as the initial_record_score on the anomaly record that happened earlier
|
||||||
assertThat(earlierRecord.getInitialRecordScore(), greaterThan(earlierRecord.getRecordScore()));
|
assertThat(earlierRecord.getInitialRecordScore(), greaterThan(earlierRecord.getRecordScore()));
|
||||||
|
|
||||||
|
// Since this job ran for 50 buckets, it's a good place to assert
|
||||||
|
// that established model memory matches model memory in the job stats
|
||||||
|
GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0);
|
||||||
|
ModelSizeStats modelSizeStats = jobStats.getModelSizeStats();
|
||||||
|
Job updatedJob = getJob(job.getId()).get(0);
|
||||||
|
assertThat(updatedJob.getEstablishedModelMemory(), equalTo(modelSizeStats.getModelBytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Job.Builder buildAndRegisterJob(String jobId, TimeValue bucketSpan) throws Exception {
|
private Job.Builder buildAndRegisterJob(String jobId, TimeValue bucketSpan) throws Exception {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||||
import org.elasticsearch.common.util.concurrent.ConcurrentMapLong;
|
import org.elasticsearch.common.util.concurrent.ConcurrentMapLong;
|
||||||
import org.elasticsearch.xpack.ml.action.DeleteDatafeedAction;
|
import org.elasticsearch.xpack.ml.action.DeleteDatafeedAction;
|
||||||
import org.elasticsearch.xpack.ml.action.GetDatafeedsStatsAction;
|
import org.elasticsearch.xpack.ml.action.GetDatafeedsStatsAction;
|
||||||
|
import org.elasticsearch.xpack.ml.action.GetJobsStatsAction;
|
||||||
import org.elasticsearch.xpack.ml.action.PutJobAction;
|
import org.elasticsearch.xpack.ml.action.PutJobAction;
|
||||||
import org.elasticsearch.xpack.ml.action.StopDatafeedAction;
|
import org.elasticsearch.xpack.ml.action.StopDatafeedAction;
|
||||||
import org.elasticsearch.xpack.ml.datafeed.DatafeedConfig;
|
import org.elasticsearch.xpack.ml.datafeed.DatafeedConfig;
|
||||||
|
@ -20,6 +21,7 @@ import org.elasticsearch.xpack.ml.datafeed.DatafeedState;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
import org.elasticsearch.xpack.ml.job.config.JobState;
|
import org.elasticsearch.xpack.ml.job.config.JobState;
|
||||||
import org.elasticsearch.xpack.ml.job.process.autodetect.state.DataCounts;
|
import org.elasticsearch.xpack.ml.job.process.autodetect.state.DataCounts;
|
||||||
|
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -85,6 +87,13 @@ public class DatafeedJobsIT extends MlNativeAutodetectIntegTestCase {
|
||||||
}, 60, TimeUnit.SECONDS);
|
}, 60, TimeUnit.SECONDS);
|
||||||
|
|
||||||
waitUntilJobIsClosed(job.getId());
|
waitUntilJobIsClosed(job.getId());
|
||||||
|
|
||||||
|
// Since this job ran for 168 buckets, it's a good place to assert
|
||||||
|
// that established model memory matches model memory in the job stats
|
||||||
|
GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0);
|
||||||
|
ModelSizeStats modelSizeStats = jobStats.getModelSizeStats();
|
||||||
|
Job updatedJob = getJob(job.getId()).get(0);
|
||||||
|
assertThat(updatedJob.getEstablishedModelMemory(), equalTo(modelSizeStats.getModelBytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRealtime() throws Exception {
|
public void testRealtime() throws Exception {
|
||||||
|
|
|
@ -21,7 +21,6 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.equalTo;
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.CoreMatchers.nullValue;
|
|
||||||
|
|
||||||
public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
|
@ -42,7 +41,7 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
initClusterAndJob(jobId);
|
initClusterAndJob(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), nullValue());
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenNoStatsLongHistory() throws Exception {
|
public void testEstablishedMem_givenNoStatsLongHistory() throws Exception {
|
||||||
|
@ -53,7 +52,7 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
createBuckets(jobId, 25);
|
createBuckets(jobId, 25);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), nullValue());
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenNoStatsShortHistory() throws Exception {
|
public void testEstablishedMem_givenNoStatsShortHistory() throws Exception {
|
||||||
|
@ -64,7 +63,7 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
createBuckets(jobId, 5);
|
createBuckets(jobId, 5);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), nullValue());
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenHistoryTooShort() throws Exception {
|
public void testEstablishedMem_givenHistoryTooShort() throws Exception {
|
||||||
|
@ -74,10 +73,11 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
createBuckets(jobId, 19);
|
createBuckets(jobId, 19);
|
||||||
createModelSizeStats(jobId, 1, 19000L);
|
createModelSizeStats(jobId, 1, 19000L);
|
||||||
createModelSizeStats(jobId, 10, 20000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 10, 20000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), nullValue());
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 19, latestModelSizeStats), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenHistoryJustEnoughLowVariation() throws Exception {
|
public void testEstablishedMem_givenHistoryJustEnoughLowVariation() throws Exception {
|
||||||
|
@ -87,10 +87,25 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
createBuckets(jobId, 20);
|
createBuckets(jobId, 20);
|
||||||
createModelSizeStats(jobId, 1, 19000L);
|
createModelSizeStats(jobId, 1, 19000L);
|
||||||
createModelSizeStats(jobId, 10, 20000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 10, 20000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 20, latestModelSizeStats), equalTo(20000L));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEstablishedMem_givenHistoryJustEnoughAndUninitialized() throws Exception {
|
||||||
|
String jobId = "just-enough-low-cv-established-mem-job";
|
||||||
|
|
||||||
|
initClusterAndJob(jobId);
|
||||||
|
|
||||||
|
createBuckets(jobId, 20);
|
||||||
|
createModelSizeStats(jobId, 1, 0L);
|
||||||
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 10, 0L);
|
||||||
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 20, latestModelSizeStats), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenHistoryJustEnoughHighVariation() throws Exception {
|
public void testEstablishedMem_givenHistoryJustEnoughHighVariation() throws Exception {
|
||||||
|
@ -100,10 +115,11 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
createBuckets(jobId, 20);
|
createBuckets(jobId, 20);
|
||||||
createModelSizeStats(jobId, 1, 1000L);
|
createModelSizeStats(jobId, 1, 1000L);
|
||||||
createModelSizeStats(jobId, 10, 20000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 10, 20000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), nullValue());
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 20, latestModelSizeStats), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenLongEstablished() throws Exception {
|
public void testEstablishedMem_givenLongEstablished() throws Exception {
|
||||||
|
@ -113,10 +129,11 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
createBuckets(jobId, 25);
|
createBuckets(jobId, 25);
|
||||||
createModelSizeStats(jobId, 1, 10000L);
|
createModelSizeStats(jobId, 1, 10000L);
|
||||||
createModelSizeStats(jobId, 2, 20000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 2, 20000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 25, latestModelSizeStats), equalTo(20000L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenOneRecentChange() throws Exception {
|
public void testEstablishedMem_givenOneRecentChange() throws Exception {
|
||||||
|
@ -126,10 +143,24 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
|
|
||||||
createBuckets(jobId, 25);
|
createBuckets(jobId, 25);
|
||||||
createModelSizeStats(jobId, 1, 10000L);
|
createModelSizeStats(jobId, 1, 10000L);
|
||||||
createModelSizeStats(jobId, 10, 20000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 10, 20000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 25, latestModelSizeStats), equalTo(20000L));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEstablishedMem_givenOneRecentChangeOnlyAndUninitialized() throws Exception {
|
||||||
|
String jobId = "one-recent-change-established-mem-job";
|
||||||
|
|
||||||
|
initClusterAndJob(jobId);
|
||||||
|
|
||||||
|
createBuckets(jobId, 25);
|
||||||
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 10, 0L);
|
||||||
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 25, latestModelSizeStats), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenOneRecentChangeOnly() throws Exception {
|
public void testEstablishedMem_givenOneRecentChangeOnly() throws Exception {
|
||||||
|
@ -138,10 +169,11 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
initClusterAndJob(jobId);
|
initClusterAndJob(jobId);
|
||||||
|
|
||||||
createBuckets(jobId, 25);
|
createBuckets(jobId, 25);
|
||||||
createModelSizeStats(jobId, 10, 20000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 10, 20000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 25, latestModelSizeStats), equalTo(20000L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenHistoricHighVariationRecentLowVariation() throws Exception {
|
public void testEstablishedMem_givenHistoricHighVariationRecentLowVariation() throws Exception {
|
||||||
|
@ -155,10 +187,11 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
createModelSizeStats(jobId, 10, 6000L);
|
createModelSizeStats(jobId, 10, 6000L);
|
||||||
createModelSizeStats(jobId, 19, 9000L);
|
createModelSizeStats(jobId, 19, 9000L);
|
||||||
createModelSizeStats(jobId, 30, 19000L);
|
createModelSizeStats(jobId, 30, 19000L);
|
||||||
createModelSizeStats(jobId, 35, 20000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 35, 20000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(20000L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 40, latestModelSizeStats), equalTo(20000L));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEstablishedMem_givenHistoricLowVariationRecentHighVariation() throws Exception {
|
public void testEstablishedMem_givenHistoricLowVariationRecentHighVariation() throws Exception {
|
||||||
|
@ -172,10 +205,11 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
createModelSizeStats(jobId, 25, 21000L);
|
createModelSizeStats(jobId, 25, 21000L);
|
||||||
createModelSizeStats(jobId, 27, 39000L);
|
createModelSizeStats(jobId, 27, 39000L);
|
||||||
createModelSizeStats(jobId, 30, 67000L);
|
createModelSizeStats(jobId, 30, 67000L);
|
||||||
createModelSizeStats(jobId, 35, 95000L);
|
ModelSizeStats latestModelSizeStats = createModelSizeStats(jobId, 35, 95000L);
|
||||||
jobResultsPersister.commitResultWrites(jobId);
|
jobResultsPersister.commitResultWrites(jobId);
|
||||||
|
|
||||||
assertThat(queryEstablishedMemoryUsage(jobId), nullValue());
|
assertThat(queryEstablishedMemoryUsage(jobId), equalTo(0L));
|
||||||
|
assertThat(queryEstablishedMemoryUsage(jobId, 40, latestModelSizeStats), equalTo(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initClusterAndJob(String jobId) {
|
private void initClusterAndJob(String jobId) {
|
||||||
|
@ -197,21 +231,28 @@ public class EstablishedMemUsageIT extends BaseMlIntegTestCase {
|
||||||
builder.executeRequest();
|
builder.executeRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createModelSizeStats(String jobId, int bucketNum, long modelBytes) {
|
private ModelSizeStats createModelSizeStats(String jobId, int bucketNum, long modelBytes) {
|
||||||
ModelSizeStats.Builder modelSizeStats = new ModelSizeStats.Builder(jobId);
|
ModelSizeStats modelSizeStats = new ModelSizeStats.Builder(jobId)
|
||||||
modelSizeStats.setTimestamp(new Date(bucketSpan * bucketNum));
|
.setTimestamp(new Date(bucketSpan * bucketNum))
|
||||||
modelSizeStats.setLogTime(new Date(bucketSpan * bucketNum + randomIntBetween(1, 1000)));
|
.setLogTime(new Date(bucketSpan * bucketNum + randomIntBetween(1, 1000)))
|
||||||
modelSizeStats.setModelBytes(modelBytes);
|
.setModelBytes(modelBytes).build();
|
||||||
jobResultsPersister.persistModelSizeStats(modelSizeStats.build());
|
jobResultsPersister.persistModelSizeStats(modelSizeStats);
|
||||||
|
return modelSizeStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long queryEstablishedMemoryUsage(String jobId) throws Exception {
|
private Long queryEstablishedMemoryUsage(String jobId) throws Exception {
|
||||||
|
return queryEstablishedMemoryUsage(jobId, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long queryEstablishedMemoryUsage(String jobId, Integer bucketNum, ModelSizeStats latestModelSizeStats)
|
||||||
|
throws Exception {
|
||||||
AtomicReference<Long> establishedModelMemoryUsage = new AtomicReference<>();
|
AtomicReference<Long> establishedModelMemoryUsage = new AtomicReference<>();
|
||||||
AtomicReference<Exception> exception = new AtomicReference<>();
|
AtomicReference<Exception> exception = new AtomicReference<>();
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
jobProvider.getEstablishedMemoryUsage(jobId, memUse -> {
|
Date latestBucketTimestamp = (bucketNum != null) ? new Date(bucketSpan * bucketNum) : null;
|
||||||
|
jobProvider.getEstablishedMemoryUsage(jobId, latestBucketTimestamp, latestModelSizeStats, memUse -> {
|
||||||
establishedModelMemoryUsage.set(memUse);
|
establishedModelMemoryUsage.set(memUse);
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}, e -> {
|
}, e -> {
|
||||||
|
|
|
@ -8,6 +8,8 @@ package org.elasticsearch.xpack.ml.integration;
|
||||||
import org.elasticsearch.action.search.SearchResponse;
|
import org.elasticsearch.action.search.SearchResponse;
|
||||||
import org.elasticsearch.action.support.IndicesOptions;
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
import org.elasticsearch.discovery.DiscoverySettings;
|
import org.elasticsearch.discovery.DiscoverySettings;
|
||||||
import org.elasticsearch.discovery.zen.FaultDetection;
|
import org.elasticsearch.discovery.zen.FaultDetection;
|
||||||
import org.elasticsearch.index.query.QueryBuilders;
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
|
@ -54,7 +56,7 @@ public class NetworkDisruptionIT extends BaseMlIntegTestCase {
|
||||||
internalCluster().ensureAtLeastNumDataNodes(5);
|
internalCluster().ensureAtLeastNumDataNodes(5);
|
||||||
ensureStableCluster(5);
|
ensureStableCluster(5);
|
||||||
|
|
||||||
Job.Builder job = createJob("relocation-job");
|
Job.Builder job = createJob("relocation-job", new ByteSizeValue(2, ByteSizeUnit.MB));
|
||||||
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
||||||
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).actionGet();
|
||||||
assertTrue(putJobResponse.isAcknowledged());
|
assertTrue(putJobResponse.isAcknowledged());
|
||||||
|
|
|
@ -7,12 +7,14 @@ package org.elasticsearch.xpack.ml.integration;
|
||||||
|
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.xpack.ml.action.GetBucketsAction;
|
import org.elasticsearch.xpack.ml.action.GetBucketsAction;
|
||||||
|
import org.elasticsearch.xpack.ml.action.GetJobsStatsAction;
|
||||||
import org.elasticsearch.xpack.ml.action.GetOverallBucketsAction;
|
import org.elasticsearch.xpack.ml.action.GetOverallBucketsAction;
|
||||||
import org.elasticsearch.xpack.ml.action.util.PageParams;
|
import org.elasticsearch.xpack.ml.action.util.PageParams;
|
||||||
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
||||||
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Detector;
|
import org.elasticsearch.xpack.ml.job.config.Detector;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
|
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -97,6 +99,13 @@ public class OverallBucketsIT extends MlNativeAutodetectIntegTestCase {
|
||||||
GetOverallBucketsAction.INSTANCE, filteredOverallBucketsRequest).actionGet();
|
GetOverallBucketsAction.INSTANCE, filteredOverallBucketsRequest).actionGet();
|
||||||
assertThat(filteredOverallBucketsResponse.getOverallBuckets().count(), equalTo(2L));
|
assertThat(filteredOverallBucketsResponse.getOverallBuckets().count(), equalTo(2L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since this job ran for 3000 buckets, it's a good place to assert
|
||||||
|
// that established model memory matches model memory in the job stats
|
||||||
|
GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0);
|
||||||
|
ModelSizeStats modelSizeStats = jobStats.getModelSizeStats();
|
||||||
|
Job updatedJob = getJob(job.getId()).get(0);
|
||||||
|
assertThat(updatedJob.getEstablishedModelMemory(), equalTo(modelSizeStats.getModelBytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Object> createRecord(long timestamp) {
|
private static Map<String, Object> createRecord(long timestamp) {
|
||||||
|
|
|
@ -11,11 +11,13 @@ import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
||||||
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Detector;
|
import org.elasticsearch.xpack.ml.job.config.Detector;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
|
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
||||||
import org.elasticsearch.xpack.ml.job.results.Bucket;
|
import org.elasticsearch.xpack.ml.job.results.Bucket;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -105,12 +107,21 @@ public class RestoreModelSnapshotIT extends MlNativeAutodetectIntegTestCase {
|
||||||
assertThat(getBuckets(splitJob.getId()).size(), equalTo(oneGoBuckets.size()));
|
assertThat(getBuckets(splitJob.getId()).size(), equalTo(oneGoBuckets.size()));
|
||||||
assertThat(getRecords(oneGoJob.getId()).isEmpty(), is(true));
|
assertThat(getRecords(oneGoJob.getId()).isEmpty(), is(true));
|
||||||
assertThat(getRecords(splitJob.getId()).isEmpty(), is(true));
|
assertThat(getRecords(splitJob.getId()).isEmpty(), is(true));
|
||||||
|
|
||||||
|
// Since these jobs ran for 72 buckets, it's a good place to assert
|
||||||
|
// that established model memory matches model memory in the job stats
|
||||||
|
for (Job.Builder job : Arrays.asList(oneGoJob, splitJob)) {
|
||||||
|
GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0);
|
||||||
|
ModelSizeStats modelSizeStats = jobStats.getModelSizeStats();
|
||||||
|
Job updatedJob = getJob(job.getId()).get(0);
|
||||||
|
assertThat(updatedJob.getEstablishedModelMemory(), equalTo(modelSizeStats.getModelBytes()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Job.Builder buildAndRegisterJob(String jobId, TimeValue bucketSpan) throws Exception {
|
private Job.Builder buildAndRegisterJob(String jobId, TimeValue bucketSpan) throws Exception {
|
||||||
Detector.Builder detector = new Detector.Builder("mean", "value");
|
Detector.Builder detector = new Detector.Builder("mean", "value");
|
||||||
detector.setByFieldName("by_field");
|
detector.setByFieldName("by_field");
|
||||||
AnalysisConfig.Builder analysisConfig = new AnalysisConfig.Builder(Arrays.asList(detector.build()));
|
AnalysisConfig.Builder analysisConfig = new AnalysisConfig.Builder(Collections.singletonList(detector.build()));
|
||||||
analysisConfig.setBucketSpan(bucketSpan);
|
analysisConfig.setBucketSpan(bucketSpan);
|
||||||
Job.Builder job = new Job.Builder(jobId);
|
Job.Builder job = new Job.Builder(jobId);
|
||||||
job.setAnalysisConfig(analysisConfig);
|
job.setAnalysisConfig(analysisConfig);
|
||||||
|
|
|
@ -9,7 +9,11 @@ import org.elasticsearch.ElasticsearchStatusException;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeUnit;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
import org.elasticsearch.transport.TransportService;
|
||||||
|
import org.elasticsearch.xpack.ml.MachineLearning;
|
||||||
import org.elasticsearch.xpack.ml.MlMetadata;
|
import org.elasticsearch.xpack.ml.MlMetadata;
|
||||||
import org.elasticsearch.xpack.ml.action.CloseJobAction;
|
import org.elasticsearch.xpack.ml.action.CloseJobAction;
|
||||||
import org.elasticsearch.xpack.ml.action.GetJobsStatsAction;
|
import org.elasticsearch.xpack.ml.action.GetJobsStatsAction;
|
||||||
|
@ -28,7 +32,7 @@ public class TooManyJobsIT extends BaseMlIntegTestCase {
|
||||||
startMlCluster(1, 1);
|
startMlCluster(1, 1);
|
||||||
|
|
||||||
// create and open first job, which succeeds:
|
// create and open first job, which succeeds:
|
||||||
Job.Builder job = createJob("close-failed-job-1");
|
Job.Builder job = createJob("close-failed-job-1", new ByteSizeValue(2, ByteSizeUnit.MB));
|
||||||
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
||||||
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).get();
|
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).get();
|
||||||
assertTrue(putJobResponse.isAcknowledged());
|
assertTrue(putJobResponse.isAcknowledged());
|
||||||
|
@ -40,7 +44,7 @@ public class TooManyJobsIT extends BaseMlIntegTestCase {
|
||||||
});
|
});
|
||||||
|
|
||||||
// create and try to open second job, which fails:
|
// create and try to open second job, which fails:
|
||||||
job = createJob("close-failed-job-2");
|
job = createJob("close-failed-job-2", new ByteSizeValue(2, ByteSizeUnit.MB));
|
||||||
putJobRequest = new PutJobAction.Request(job);
|
putJobRequest = new PutJobAction.Request(job);
|
||||||
putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).get();
|
putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).get();
|
||||||
assertTrue(putJobResponse.isAcknowledged());
|
assertTrue(putJobResponse.isAcknowledged());
|
||||||
|
@ -60,18 +64,23 @@ public class TooManyJobsIT extends BaseMlIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSingleNode() throws Exception {
|
public void testSingleNode() throws Exception {
|
||||||
verifyMaxNumberOfJobsLimit(1, randomIntBetween(1, 32));
|
verifyMaxNumberOfJobsLimit(1, randomIntBetween(1, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultipleNodes() throws Exception {
|
public void testMultipleNodes() throws Exception {
|
||||||
verifyMaxNumberOfJobsLimit(3, randomIntBetween(1, 32));
|
verifyMaxNumberOfJobsLimit(3, randomIntBetween(1, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyMaxNumberOfJobsLimit(int numNodes, int maxNumberOfJobsPerNode) throws Exception {
|
private void verifyMaxNumberOfJobsLimit(int numNodes, int maxNumberOfJobsPerNode) throws Exception {
|
||||||
startMlCluster(numNodes, maxNumberOfJobsPerNode);
|
startMlCluster(numNodes, maxNumberOfJobsPerNode);
|
||||||
|
long maxMlMemoryPerNode = calculateMaxMlMemory();
|
||||||
|
ByteSizeValue jobModelMemoryLimit = new ByteSizeValue(2, ByteSizeUnit.MB);
|
||||||
|
long memoryFootprintPerJob = jobModelMemoryLimit.getBytes() + Job.PROCESS_MEMORY_OVERHEAD.getBytes();
|
||||||
|
long maxJobsPerNodeDueToMemoryLimit = maxMlMemoryPerNode / memoryFootprintPerJob;
|
||||||
int clusterWideMaxNumberOfJobs = numNodes * maxNumberOfJobsPerNode;
|
int clusterWideMaxNumberOfJobs = numNodes * maxNumberOfJobsPerNode;
|
||||||
|
boolean expectMemoryLimitBeforeCountLimit = maxJobsPerNodeDueToMemoryLimit < maxNumberOfJobsPerNode;
|
||||||
for (int i = 1; i <= (clusterWideMaxNumberOfJobs + 1); i++) {
|
for (int i = 1; i <= (clusterWideMaxNumberOfJobs + 1); i++) {
|
||||||
Job.Builder job = createJob("max-number-of-jobs-limit-job-" + Integer.toString(i));
|
Job.Builder job = createJob("max-number-of-jobs-limit-job-" + Integer.toString(i), jobModelMemoryLimit);
|
||||||
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
PutJobAction.Request putJobRequest = new PutJobAction.Request(job);
|
||||||
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).get();
|
PutJobAction.Response putJobResponse = client().execute(PutJobAction.INSTANCE, putJobRequest).get();
|
||||||
assertTrue(putJobResponse.isAcknowledged());
|
assertTrue(putJobResponse.isAcknowledged());
|
||||||
|
@ -86,9 +95,19 @@ public class TooManyJobsIT extends BaseMlIntegTestCase {
|
||||||
});
|
});
|
||||||
logger.info("Opened {}th job", i);
|
logger.info("Opened {}th job", i);
|
||||||
} catch (ElasticsearchStatusException e) {
|
} catch (ElasticsearchStatusException e) {
|
||||||
assertTrue(e.getMessage(), e.getMessage().startsWith("Could not open job because no suitable nodes were found, allocation explanation"));
|
assertTrue(e.getMessage(),
|
||||||
assertTrue(e.getMessage(), e.getMessage().endsWith("because this node is full. Number of opened jobs [" + maxNumberOfJobsPerNode +
|
e.getMessage().startsWith("Could not open job because no suitable nodes were found, allocation explanation"));
|
||||||
"], xpack.ml.max_open_jobs [" + maxNumberOfJobsPerNode + "]]"));
|
if (expectMemoryLimitBeforeCountLimit) {
|
||||||
|
int expectedJobsAlreadyOpenOnNode = (i - 1) / numNodes;
|
||||||
|
assertTrue(e.getMessage(),
|
||||||
|
e.getMessage().endsWith("because this node has insufficient available memory. Available memory for ML [" +
|
||||||
|
maxMlMemoryPerNode + "], memory required by existing jobs [" +
|
||||||
|
(expectedJobsAlreadyOpenOnNode * memoryFootprintPerJob) +
|
||||||
|
"], estimated memory required for this job [" + memoryFootprintPerJob + "]]"));
|
||||||
|
} else {
|
||||||
|
assertTrue(e.getMessage(), e.getMessage().endsWith("because this node is full. Number of opened jobs [" +
|
||||||
|
maxNumberOfJobsPerNode + "], xpack.ml.max_open_jobs [" + maxNumberOfJobsPerNode + "]]"));
|
||||||
|
}
|
||||||
logger.info("good news everybody --> reached maximum number of allowed opened jobs, after trying to open the {}th job", i);
|
logger.info("good news everybody --> reached maximum number of allowed opened jobs, after trying to open the {}th job", i);
|
||||||
|
|
||||||
// close the first job and check if the latest job gets opened:
|
// close the first job and check if the latest job gets opened:
|
||||||
|
@ -122,4 +141,9 @@ public class TooManyJobsIT extends BaseMlIntegTestCase {
|
||||||
ensureStableCluster(numNodes);
|
ensureStableCluster(numNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long calculateMaxMlMemory() {
|
||||||
|
Settings settings = internalCluster().getInstance(Settings.class);
|
||||||
|
return Long.parseLong(internalCluster().getInstance(TransportService.class).getLocalNode().getAttributes()
|
||||||
|
.get(MachineLearning.MACHINE_MEMORY_NODE_ATTR)) * MachineLearning.MAX_MACHINE_MEMORY_PERCENT.get(settings) / 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,9 +431,10 @@ public class JobTests extends AbstractSerializingTestCase<Job> {
|
||||||
public void testBuilder_buildWithCreateTime() {
|
public void testBuilder_buildWithCreateTime() {
|
||||||
Job.Builder builder = buildJobBuilder("foo");
|
Job.Builder builder = buildJobBuilder("foo");
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
Job job = builder.build(now);
|
Job job = builder.setEstablishedModelMemory(randomNonNegativeLong()).build(now);
|
||||||
assertEquals(now, job.getCreateTime());
|
assertEquals(now, job.getCreateTime());
|
||||||
assertEquals(Version.CURRENT, job.getJobVersion());
|
assertEquals(Version.CURRENT, job.getJobVersion());
|
||||||
|
assertNull(job.getEstablishedModelMemory());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testJobWithoutVersion() throws IOException {
|
public void testJobWithoutVersion() throws IOException {
|
||||||
|
@ -516,6 +517,39 @@ public class JobTests extends AbstractSerializingTestCase<Job> {
|
||||||
assertThat(e.getMessage(), containsString("Invalid group id '$$$'"));
|
assertThat(e.getMessage(), containsString("Invalid group id '$$$'"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEstimateMemoryFootprint_GivenEstablished() {
|
||||||
|
Job.Builder builder = buildJobBuilder("established");
|
||||||
|
long establishedModelMemory = randomIntBetween(10_000, 2_000_000_000);
|
||||||
|
builder.setEstablishedModelMemory(establishedModelMemory);
|
||||||
|
if (randomBoolean()) {
|
||||||
|
builder.setAnalysisLimits(new AnalysisLimits(randomNonNegativeLong(), null));
|
||||||
|
}
|
||||||
|
assertEquals(establishedModelMemory + Job.PROCESS_MEMORY_OVERHEAD.getBytes(), builder.build().estimateMemoryFootprint());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEstimateMemoryFootprint_GivenLimitAndNotEstablished() {
|
||||||
|
Job.Builder builder = buildJobBuilder("limit");
|
||||||
|
if (rarely()) {
|
||||||
|
// An "established" model memory of 0 means "not established". Generally this won't be set, so getEstablishedModelMemory()
|
||||||
|
// will return null, but if it returns 0 we shouldn't estimate the job's memory requirement to be 0.
|
||||||
|
builder.setEstablishedModelMemory(0L);
|
||||||
|
}
|
||||||
|
ByteSizeValue limit = new ByteSizeValue(randomIntBetween(100, 10000), ByteSizeUnit.MB);
|
||||||
|
builder.setAnalysisLimits(new AnalysisLimits(limit.getMb(), null));
|
||||||
|
assertEquals(limit.getBytes() + Job.PROCESS_MEMORY_OVERHEAD.getBytes(), builder.build().estimateMemoryFootprint());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEstimateMemoryFootprint_GivenNoLimitAndNotEstablished() {
|
||||||
|
Job.Builder builder = buildJobBuilder("nolimit");
|
||||||
|
if (rarely()) {
|
||||||
|
// An "established" model memory of 0 means "not established". Generally this won't be set, so getEstablishedModelMemory()
|
||||||
|
// will return null, but if it returns 0 we shouldn't estimate the job's memory requirement to be 0.
|
||||||
|
builder.setEstablishedModelMemory(0L);
|
||||||
|
}
|
||||||
|
assertEquals(ByteSizeUnit.MB.toBytes(JobUpdate.UNDEFINED_MODEL_MEMORY_LIMIT_DEFAULT) + Job.PROCESS_MEMORY_OVERHEAD.getBytes(),
|
||||||
|
builder.build().estimateMemoryFootprint());
|
||||||
|
}
|
||||||
|
|
||||||
public static Job.Builder buildJobBuilder(String id, Date date) {
|
public static Job.Builder buildJobBuilder(String id, Date date) {
|
||||||
Job.Builder builder = new Job.Builder(id);
|
Job.Builder builder = new Job.Builder(id);
|
||||||
builder.setCreateTime(date);
|
builder.setCreateTime(date);
|
||||||
|
@ -566,6 +600,9 @@ public class JobTests extends AbstractSerializingTestCase<Job> {
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
builder.setLastDataTime(new Date(randomNonNegativeLong()));
|
builder.setLastDataTime(new Date(randomNonNegativeLong()));
|
||||||
}
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
builder.setEstablishedModelMemory(randomNonNegativeLong());
|
||||||
|
}
|
||||||
builder.setAnalysisConfig(AnalysisConfigTests.createRandomized());
|
builder.setAnalysisConfig(AnalysisConfigTests.createRandomized());
|
||||||
builder.setAnalysisLimits(AnalysisLimitsTests.createRandomized());
|
builder.setAnalysisLimits(AnalysisLimitsTests.createRandomized());
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,9 @@ public class JobUpdateTests extends AbstractSerializingTestCase<JobUpdate> {
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
update.setModelSnapshotId(randomAlphaOfLength(10));
|
update.setModelSnapshotId(randomAlphaOfLength(10));
|
||||||
}
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
update.setEstablishedModelMemory(randomNonNegativeLong());
|
||||||
|
}
|
||||||
|
|
||||||
return update.build();
|
return update.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,12 @@ package org.elasticsearch.xpack.ml.job.process.autodetect.output;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
|
import org.elasticsearch.action.support.WriteRequest;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.ml.action.UpdateJobAction;
|
import org.elasticsearch.xpack.ml.action.UpdateJobAction;
|
||||||
import org.elasticsearch.xpack.ml.job.config.JobUpdate;
|
import org.elasticsearch.xpack.ml.job.config.JobUpdate;
|
||||||
|
import org.elasticsearch.xpack.ml.job.persistence.JobProvider;
|
||||||
import org.elasticsearch.xpack.ml.job.persistence.JobResultsPersister;
|
import org.elasticsearch.xpack.ml.job.persistence.JobResultsPersister;
|
||||||
import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcess;
|
import org.elasticsearch.xpack.ml.job.process.autodetect.AutodetectProcess;
|
||||||
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
import org.elasticsearch.xpack.ml.job.process.autodetect.state.ModelSizeStats;
|
||||||
|
@ -33,11 +35,13 @@ import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Matchers.isNull;
|
||||||
import static org.mockito.Matchers.same;
|
import static org.mockito.Matchers.same;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
@ -55,6 +59,7 @@ public class AutoDetectResultProcessorTests extends ESTestCase {
|
||||||
private Client client;
|
private Client client;
|
||||||
private Renormalizer renormalizer;
|
private Renormalizer renormalizer;
|
||||||
private JobResultsPersister persister;
|
private JobResultsPersister persister;
|
||||||
|
private JobProvider jobProvider;
|
||||||
private FlushListener flushListener;
|
private FlushListener flushListener;
|
||||||
private AutoDetectResultProcessor processorUnderTest;
|
private AutoDetectResultProcessor processorUnderTest;
|
||||||
|
|
||||||
|
@ -63,12 +68,15 @@ public class AutoDetectResultProcessorTests extends ESTestCase {
|
||||||
client = mock(Client.class);
|
client = mock(Client.class);
|
||||||
renormalizer = mock(Renormalizer.class);
|
renormalizer = mock(Renormalizer.class);
|
||||||
persister = mock(JobResultsPersister.class);
|
persister = mock(JobResultsPersister.class);
|
||||||
|
jobProvider = mock(JobProvider.class);
|
||||||
flushListener = mock(FlushListener.class);
|
flushListener = mock(FlushListener.class);
|
||||||
processorUnderTest = new AutoDetectResultProcessor(client, JOB_ID, renormalizer, persister,
|
processorUnderTest = new AutoDetectResultProcessor(client, JOB_ID, renormalizer, persister, jobProvider,
|
||||||
new ModelSizeStats.Builder(JOB_ID).build(), flushListener);
|
new ModelSizeStats.Builder(JOB_ID).build(), false, flushListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testProcess() throws TimeoutException {
|
public void testProcess() throws TimeoutException {
|
||||||
|
JobResultsPersister.Builder bulkBuilder = mock(JobResultsPersister.Builder.class);
|
||||||
|
when(persister.bulkPersisterBuilder(JOB_ID)).thenReturn(bulkBuilder);
|
||||||
AutodetectResult autodetectResult = mock(AutodetectResult.class);
|
AutodetectResult autodetectResult = mock(AutodetectResult.class);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Iterator<AutodetectResult> iterator = mock(Iterator.class);
|
Iterator<AutodetectResult> iterator = mock(Iterator.class);
|
||||||
|
@ -259,6 +267,36 @@ public class AutoDetectResultProcessorTests extends ESTestCase {
|
||||||
|
|
||||||
verify(persister, times(1)).persistModelSizeStats(modelSizeStats);
|
verify(persister, times(1)).persistModelSizeStats(modelSizeStats);
|
||||||
verifyNoMoreInteractions(persister);
|
verifyNoMoreInteractions(persister);
|
||||||
|
// No interactions with the jobProvider confirms that the established memory calculation did not run
|
||||||
|
verifyNoMoreInteractions(jobProvider);
|
||||||
|
assertEquals(modelSizeStats, processorUnderTest.modelSizeStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testProcessResult_modelSizeStatsAfterManyBuckets() {
|
||||||
|
JobResultsPersister.Builder bulkBuilder = mock(JobResultsPersister.Builder.class);
|
||||||
|
when(persister.bulkPersisterBuilder(JOB_ID)).thenReturn(bulkBuilder);
|
||||||
|
when(bulkBuilder.persistBucket(any(Bucket.class))).thenReturn(bulkBuilder);
|
||||||
|
|
||||||
|
AutoDetectResultProcessor.Context context = new AutoDetectResultProcessor.Context(JOB_ID, bulkBuilder);
|
||||||
|
context.deleteInterimRequired = false;
|
||||||
|
for (int i = 0; i < JobProvider.BUCKETS_FOR_ESTABLISHED_MEMORY_SIZE; ++i) {
|
||||||
|
AutodetectResult result = mock(AutodetectResult.class);
|
||||||
|
Bucket bucket = mock(Bucket.class);
|
||||||
|
when(result.getBucket()).thenReturn(bucket);
|
||||||
|
processorUnderTest.processResult(context, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutodetectResult result = mock(AutodetectResult.class);
|
||||||
|
ModelSizeStats modelSizeStats = mock(ModelSizeStats.class);
|
||||||
|
when(result.getModelSizeStats()).thenReturn(modelSizeStats);
|
||||||
|
processorUnderTest.processResult(context, result);
|
||||||
|
|
||||||
|
verify(persister, times(1)).persistModelSizeStats(modelSizeStats);
|
||||||
|
verify(persister, times(1)).commitResultWrites(JOB_ID);
|
||||||
|
verifyNoMoreInteractions(persister);
|
||||||
|
verify(jobProvider, times(1)).getEstablishedMemoryUsage(eq(JOB_ID), isNull(Date.class), eq(modelSizeStats),
|
||||||
|
any(Consumer.class), any(Consumer.class));
|
||||||
|
verifyNoMoreInteractions(jobProvider);
|
||||||
assertEquals(modelSizeStats, processorUnderTest.modelSizeStats());
|
assertEquals(modelSizeStats, processorUnderTest.modelSizeStats());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,12 +311,11 @@ public class AutoDetectResultProcessorTests extends ESTestCase {
|
||||||
when(result.getModelSnapshot()).thenReturn(modelSnapshot);
|
when(result.getModelSnapshot()).thenReturn(modelSnapshot);
|
||||||
processorUnderTest.processResult(context, result);
|
processorUnderTest.processResult(context, result);
|
||||||
|
|
||||||
verify(persister, times(1)).persistModelSnapshot(modelSnapshot);
|
verify(persister, times(1)).persistModelSnapshot(modelSnapshot, WriteRequest.RefreshPolicy.IMMEDIATE);
|
||||||
UpdateJobAction.Request expectedJobUpdateRequest = new UpdateJobAction.Request(JOB_ID,
|
UpdateJobAction.Request expectedJobUpdateRequest = new UpdateJobAction.Request(JOB_ID,
|
||||||
new JobUpdate.Builder(JOB_ID).setModelSnapshotId("a_snapshot_id").build());
|
new JobUpdate.Builder(JOB_ID).setModelSnapshotId("a_snapshot_id").build());
|
||||||
|
|
||||||
verify(client).execute(same(UpdateJobAction.INSTANCE), eq(expectedJobUpdateRequest), any());
|
verify(client).execute(same(UpdateJobAction.INSTANCE), eq(expectedJobUpdateRequest), any());
|
||||||
verify(persister).commitResultWrites(JOB_ID);
|
|
||||||
verifyNoMoreInteractions(persister);
|
verifyNoMoreInteractions(persister);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,6 +338,8 @@ public class AutoDetectResultProcessorTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAwaitCompletion() throws TimeoutException {
|
public void testAwaitCompletion() throws TimeoutException {
|
||||||
|
JobResultsPersister.Builder bulkBuilder = mock(JobResultsPersister.Builder.class);
|
||||||
|
when(persister.bulkPersisterBuilder(JOB_ID)).thenReturn(bulkBuilder);
|
||||||
AutodetectResult autodetectResult = mock(AutodetectResult.class);
|
AutodetectResult autodetectResult = mock(AutodetectResult.class);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Iterator<AutodetectResult> iterator = mock(Iterator.class);
|
Iterator<AutodetectResult> iterator = mock(Iterator.class);
|
||||||
|
@ -316,6 +355,8 @@ public class AutoDetectResultProcessorTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPersisterThrowingDoesntBlockProcessing() {
|
public void testPersisterThrowingDoesntBlockProcessing() {
|
||||||
|
JobResultsPersister.Builder bulkBuilder = mock(JobResultsPersister.Builder.class);
|
||||||
|
when(persister.bulkPersisterBuilder(JOB_ID)).thenReturn(bulkBuilder);
|
||||||
AutodetectResult autodetectResult = mock(AutodetectResult.class);
|
AutodetectResult autodetectResult = mock(AutodetectResult.class);
|
||||||
ModelSnapshot modelSnapshot = mock(ModelSnapshot.class);
|
ModelSnapshot modelSnapshot = mock(ModelSnapshot.class);
|
||||||
when(autodetectResult.getModelSnapshot()).thenReturn(modelSnapshot);
|
when(autodetectResult.getModelSnapshot()).thenReturn(modelSnapshot);
|
||||||
|
@ -329,10 +370,10 @@ public class AutoDetectResultProcessorTests extends ESTestCase {
|
||||||
when(process.isProcessAliveAfterWaiting()).thenReturn(true);
|
when(process.isProcessAliveAfterWaiting()).thenReturn(true);
|
||||||
when(process.readAutodetectResults()).thenReturn(iterator);
|
when(process.readAutodetectResults()).thenReturn(iterator);
|
||||||
|
|
||||||
doThrow(new ElasticsearchException("this test throws")).when(persister).persistModelSnapshot(any());
|
doThrow(new ElasticsearchException("this test throws")).when(persister).persistModelSnapshot(any(), any());
|
||||||
|
|
||||||
processorUnderTest.process(process);
|
processorUnderTest.process(process);
|
||||||
verify(persister, times(2)).persistModelSnapshot(any());
|
verify(persister, times(2)).persistModelSnapshot(any(), eq(WriteRequest.RefreshPolicy.IMMEDIATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParsingErrorSetsFailed() {
|
public void testParsingErrorSetsFailed() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.analysis.common.CommonAnalysisPlugin;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.index.reindex.ReindexPlugin;
|
import org.elasticsearch.index.reindex.ReindexPlugin;
|
||||||
import org.elasticsearch.indices.recovery.RecoveryState;
|
import org.elasticsearch.indices.recovery.RecoveryState;
|
||||||
|
@ -36,6 +37,7 @@ import org.elasticsearch.xpack.ml.action.StopDatafeedAction;
|
||||||
import org.elasticsearch.xpack.ml.datafeed.DatafeedConfig;
|
import org.elasticsearch.xpack.ml.datafeed.DatafeedConfig;
|
||||||
import org.elasticsearch.xpack.ml.datafeed.DatafeedState;
|
import org.elasticsearch.xpack.ml.datafeed.DatafeedState;
|
||||||
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
import org.elasticsearch.xpack.ml.job.config.AnalysisConfig;
|
||||||
|
import org.elasticsearch.xpack.ml.job.config.AnalysisLimits;
|
||||||
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
import org.elasticsearch.xpack.ml.job.config.DataDescription;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Detector;
|
import org.elasticsearch.xpack.ml.job.config.Detector;
|
||||||
import org.elasticsearch.xpack.ml.job.config.Job;
|
import org.elasticsearch.xpack.ml.job.config.Job;
|
||||||
|
@ -118,6 +120,10 @@ public abstract class BaseMlIntegTestCase extends ESIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Job.Builder createJob(String id) {
|
protected Job.Builder createJob(String id) {
|
||||||
|
return createJob(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Job.Builder createJob(String id, ByteSizeValue modelMemoryLimit) {
|
||||||
DataDescription.Builder dataDescription = new DataDescription.Builder();
|
DataDescription.Builder dataDescription = new DataDescription.Builder();
|
||||||
dataDescription.setFormat(DataDescription.DataFormat.XCONTENT);
|
dataDescription.setFormat(DataDescription.DataFormat.XCONTENT);
|
||||||
dataDescription.setTimeFormat(DataDescription.EPOCH_MS);
|
dataDescription.setTimeFormat(DataDescription.EPOCH_MS);
|
||||||
|
@ -127,13 +133,19 @@ public abstract class BaseMlIntegTestCase extends ESIntegTestCase {
|
||||||
|
|
||||||
Job.Builder builder = new Job.Builder();
|
Job.Builder builder = new Job.Builder();
|
||||||
builder.setId(id);
|
builder.setId(id);
|
||||||
|
if (modelMemoryLimit != null) {
|
||||||
|
builder.setAnalysisLimits(new AnalysisLimits(modelMemoryLimit.getMb(), null));
|
||||||
|
}
|
||||||
builder.setAnalysisConfig(analysisConfig);
|
builder.setAnalysisConfig(analysisConfig);
|
||||||
builder.setDataDescription(dataDescription);
|
builder.setDataDescription(dataDescription);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Job.Builder createFareQuoteJob(String id) {
|
public static Job.Builder createFareQuoteJob(String id) {
|
||||||
|
return createFareQuoteJob(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Job.Builder createFareQuoteJob(String id, ByteSizeValue modelMemoryLimit) {
|
||||||
DataDescription.Builder dataDescription = new DataDescription.Builder();
|
DataDescription.Builder dataDescription = new DataDescription.Builder();
|
||||||
dataDescription.setFormat(DataDescription.DataFormat.XCONTENT);
|
dataDescription.setFormat(DataDescription.DataFormat.XCONTENT);
|
||||||
dataDescription.setTimeFormat(DataDescription.EPOCH);
|
dataDescription.setTimeFormat(DataDescription.EPOCH);
|
||||||
|
@ -146,6 +158,9 @@ public abstract class BaseMlIntegTestCase extends ESIntegTestCase {
|
||||||
|
|
||||||
Job.Builder builder = new Job.Builder();
|
Job.Builder builder = new Job.Builder();
|
||||||
builder.setId(id);
|
builder.setId(id);
|
||||||
|
if (modelMemoryLimit != null) {
|
||||||
|
builder.setAnalysisLimits(new AnalysisLimits(modelMemoryLimit.getMb(), null));
|
||||||
|
}
|
||||||
builder.setAnalysisConfig(analysisConfig);
|
builder.setAnalysisConfig(analysisConfig);
|
||||||
builder.setDataDescription(dataDescription);
|
builder.setDataDescription(dataDescription);
|
||||||
return builder;
|
return builder;
|
||||||
|
|
|
@ -75,7 +75,8 @@ public class InternalClientIntegTests extends ESSingleNodeTestCase {
|
||||||
String scrollId = randomAlphaOfLength(5);
|
String scrollId = randomAlphaOfLength(5);
|
||||||
SearchHit[] hits = new SearchHit[] {new SearchHit(1)};
|
SearchHit[] hits = new SearchHit[] {new SearchHit(1)};
|
||||||
InternalSearchResponse internalResponse = new InternalSearchResponse(new SearchHits(hits, 1, 1), null, null, null, false, false, 1);
|
InternalSearchResponse internalResponse = new InternalSearchResponse(new SearchHits(hits, 1, 1), null, null, null, false, false, 1);
|
||||||
SearchResponse response = new SearchResponse(internalResponse, scrollId, 1, 1, 0, 0, ShardSearchFailure.EMPTY_ARRAY);
|
SearchResponse response = new SearchResponse(internalResponse, scrollId, 1, 1, 0, 0, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
|
|
||||||
Answer<?> returnResponse = invocation -> {
|
Answer<?> returnResponse = invocation -> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
|
@ -140,7 +140,7 @@ public class WatcherServiceTests extends ESTestCase {
|
||||||
// empty scroll response, no further scrolling needed
|
// empty scroll response, no further scrolling needed
|
||||||
SearchResponseSections scrollSearchSections = new SearchResponseSections(SearchHits.empty(), null, null, false, false, null, 1);
|
SearchResponseSections scrollSearchSections = new SearchResponseSections(SearchHits.empty(), null, null, false, false, null, 1);
|
||||||
SearchResponse scrollSearchResponse = new SearchResponse(scrollSearchSections, "scrollId", 1, 1, 0, 10,
|
SearchResponse scrollSearchResponse = new SearchResponse(scrollSearchSections, "scrollId", 1, 1, 0, 10,
|
||||||
ShardSearchFailure.EMPTY_ARRAY);
|
ShardSearchFailure.EMPTY_ARRAY, SearchResponse.Clusters.EMPTY);
|
||||||
|
|
||||||
// one search response containing active and inactive watches
|
// one search response containing active and inactive watches
|
||||||
int count = randomIntBetween(2, 200);
|
int count = randomIntBetween(2, 200);
|
||||||
|
@ -166,7 +166,8 @@ public class WatcherServiceTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
SearchHits searchHits = new SearchHits(hits, count, 1.0f);
|
SearchHits searchHits = new SearchHits(hits, count, 1.0f);
|
||||||
SearchResponseSections sections = new SearchResponseSections(searchHits, null, null, false, false, null, 1);
|
SearchResponseSections sections = new SearchResponseSections(searchHits, null, null, false, false, null, 1);
|
||||||
SearchResponse searchResponse = new SearchResponse(sections, "scrollId", 1, 1, 0, 10, ShardSearchFailure.EMPTY_ARRAY);
|
SearchResponse searchResponse = new SearchResponse(sections, "scrollId", 1, 1, 0, 10, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
|
|
||||||
// we do need to to use this kind of mocking because of the internal client, which calls doExecute at the end on the supplied
|
// we do need to to use this kind of mocking because of the internal client, which calls doExecute at the end on the supplied
|
||||||
// client instance
|
// client instance
|
||||||
|
|
|
@ -84,7 +84,8 @@ public class CompareConditionSearchTests extends AbstractWatcherIntegrationTestC
|
||||||
|
|
||||||
InternalSearchResponse internalSearchResponse = new InternalSearchResponse(
|
InternalSearchResponse internalSearchResponse = new InternalSearchResponse(
|
||||||
new SearchHits(new SearchHit[]{hit}, 1L, 1f), null, null, null, false, false, 1);
|
new SearchHits(new SearchHit[]{hit}, 1L, 1f), null, null, null, false, false, 1);
|
||||||
SearchResponse response = new SearchResponse(internalSearchResponse, "", 3, 3, 0, 500L, new ShardSearchFailure[0]);
|
SearchResponse response = new SearchResponse(internalSearchResponse, "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
|
|
||||||
WatchExecutionContext ctx = mockExecutionContext("_watch_name", new Payload.XContent(response));
|
WatchExecutionContext ctx = mockExecutionContext("_watch_name", new Payload.XContent(response));
|
||||||
assertThat(condition.execute(ctx).met(), is(true));
|
assertThat(condition.execute(ctx).met(), is(true));
|
||||||
|
|
|
@ -94,7 +94,8 @@ public class ScriptConditionTests extends ESTestCase {
|
||||||
|
|
||||||
public void testExecute() throws Exception {
|
public void testExecute() throws Exception {
|
||||||
ScriptCondition condition = new ScriptCondition(mockScript("ctx.payload.hits.total > 1"), scriptService);
|
ScriptCondition condition = new ScriptCondition(mockScript("ctx.payload.hits.total > 1"), scriptService);
|
||||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, new ShardSearchFailure[0]);
|
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||||
assertFalse(condition.execute(ctx).met());
|
assertFalse(condition.execute(ctx).met());
|
||||||
}
|
}
|
||||||
|
@ -102,7 +103,8 @@ public class ScriptConditionTests extends ESTestCase {
|
||||||
public void testExecuteMergedParams() throws Exception {
|
public void testExecuteMergedParams() throws Exception {
|
||||||
Script script = new Script(ScriptType.INLINE, "mockscript", "ctx.payload.hits.total > threshold", singletonMap("threshold", 1));
|
Script script = new Script(ScriptType.INLINE, "mockscript", "ctx.payload.hits.total > threshold", singletonMap("threshold", 1));
|
||||||
ScriptCondition executable = new ScriptCondition(script, scriptService);
|
ScriptCondition executable = new ScriptCondition(script, scriptService);
|
||||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, new ShardSearchFailure[0]);
|
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||||
assertFalse(executable.execute(ctx).met());
|
assertFalse(executable.execute(ctx).met());
|
||||||
}
|
}
|
||||||
|
@ -115,7 +117,8 @@ public class ScriptConditionTests extends ESTestCase {
|
||||||
parser.nextToken();
|
parser.nextToken();
|
||||||
ScriptCondition executable = ScriptCondition.parse(scriptService, "_watch", parser);
|
ScriptCondition executable = ScriptCondition.parse(scriptService, "_watch", parser);
|
||||||
|
|
||||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, new ShardSearchFailure[0]);
|
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||||
|
|
||||||
assertFalse(executable.execute(ctx).met());
|
assertFalse(executable.execute(ctx).met());
|
||||||
|
@ -179,7 +182,8 @@ public class ScriptConditionTests extends ESTestCase {
|
||||||
public void testScriptConditionThrowException() throws Exception {
|
public void testScriptConditionThrowException() throws Exception {
|
||||||
ScriptCondition condition = new ScriptCondition(
|
ScriptCondition condition = new ScriptCondition(
|
||||||
mockScript("null.foo"), scriptService);
|
mockScript("null.foo"), scriptService);
|
||||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, new ShardSearchFailure[0]);
|
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||||
ScriptException exception = expectThrows(ScriptException.class, () -> condition.execute(ctx));
|
ScriptException exception = expectThrows(ScriptException.class, () -> condition.execute(ctx));
|
||||||
assertThat(exception.getMessage(), containsString("Error evaluating null.foo"));
|
assertThat(exception.getMessage(), containsString("Error evaluating null.foo"));
|
||||||
|
@ -187,7 +191,8 @@ public class ScriptConditionTests extends ESTestCase {
|
||||||
|
|
||||||
public void testScriptConditionReturnObjectThrowsException() throws Exception {
|
public void testScriptConditionReturnObjectThrowsException() throws Exception {
|
||||||
ScriptCondition condition = new ScriptCondition(mockScript("return new Object()"), scriptService);
|
ScriptCondition condition = new ScriptCondition(mockScript("return new Object()"), scriptService);
|
||||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, new ShardSearchFailure[0]);
|
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||||
Exception exception = expectThrows(IllegalStateException.class, () -> condition.execute(ctx));
|
Exception exception = expectThrows(IllegalStateException.class, () -> condition.execute(ctx));
|
||||||
assertThat(exception.getMessage(),
|
assertThat(exception.getMessage(),
|
||||||
|
@ -197,7 +202,8 @@ public class ScriptConditionTests extends ESTestCase {
|
||||||
public void testScriptConditionAccessCtx() throws Exception {
|
public void testScriptConditionAccessCtx() throws Exception {
|
||||||
ScriptCondition condition = new ScriptCondition(mockScript("ctx.trigger.scheduled_time.getMillis() < new Date().time"),
|
ScriptCondition condition = new ScriptCondition(mockScript("ctx.trigger.scheduled_time.getMillis() < new Date().time"),
|
||||||
scriptService);
|
scriptService);
|
||||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, new ShardSearchFailure[0]);
|
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 0, 500L, ShardSearchFailure.EMPTY_ARRAY,
|
||||||
|
SearchResponse.Clusters.EMPTY);
|
||||||
WatchExecutionContext ctx = mockExecutionContext("_name", new DateTime(DateTimeZone.UTC), new Payload.XContent(response));
|
WatchExecutionContext ctx = mockExecutionContext("_name", new DateTime(DateTimeZone.UTC), new Payload.XContent(response));
|
||||||
Thread.sleep(10);
|
Thread.sleep(10);
|
||||||
assertThat(condition.execute(ctx).met(), is(true));
|
assertThat(condition.execute(ctx).met(), is(true));
|
||||||
|
|
|
@ -199,8 +199,8 @@ public class TriggeredWatchStoreTests extends ESTestCase {
|
||||||
hit.sourceRef(source);
|
hit.sourceRef(source);
|
||||||
hits = new SearchHits(new SearchHit[]{hit}, 1, 1.0f);
|
hits = new SearchHits(new SearchHit[]{hit}, 1, 1.0f);
|
||||||
SearchResponse searchResponse2 = new SearchResponse(
|
SearchResponse searchResponse2 = new SearchResponse(
|
||||||
new InternalSearchResponse(hits, null, null, null, false, null, 1), "_scrollId1", 1, 1, 0, 1, null);
|
new InternalSearchResponse(hits, null, null, null, false, null, 1), "_scrollId1", 1, 1, 0, 1, null, null);
|
||||||
SearchResponse searchResponse3 = new SearchResponse(InternalSearchResponse.empty(), "_scrollId2", 1, 1, 0, 1, null);
|
SearchResponse searchResponse3 = new SearchResponse(InternalSearchResponse.empty(), "_scrollId2", 1, 1, 0, 1, null, null);
|
||||||
|
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
SearchScrollRequest request = (SearchScrollRequest) invocation.getArguments()[0];
|
SearchScrollRequest request = (SearchScrollRequest) invocation.getArguments()[0];
|
||||||
|
|
|
@ -81,7 +81,7 @@ public class SearchInputTests extends ESTestCase {
|
||||||
ArgumentCaptor<SearchRequest> requestCaptor = ArgumentCaptor.forClass(SearchRequest.class);
|
ArgumentCaptor<SearchRequest> requestCaptor = ArgumentCaptor.forClass(SearchRequest.class);
|
||||||
PlainActionFuture<SearchResponse> searchFuture = PlainActionFuture.newFuture();
|
PlainActionFuture<SearchResponse> searchFuture = PlainActionFuture.newFuture();
|
||||||
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), "", 1, 1, 0, 1234,
|
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), "", 1, 1, 0, 1234,
|
||||||
ShardSearchFailure.EMPTY_ARRAY);
|
ShardSearchFailure.EMPTY_ARRAY, SearchResponse.Clusters.EMPTY);
|
||||||
searchFuture.onResponse(searchResponse);
|
searchFuture.onResponse(searchResponse);
|
||||||
when(client.search(requestCaptor.capture())).thenReturn(searchFuture);
|
when(client.search(requestCaptor.capture())).thenReturn(searchFuture);
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ public class SearchInputTests extends ESTestCase {
|
||||||
ArgumentCaptor<SearchRequest> requestCaptor = ArgumentCaptor.forClass(SearchRequest.class);
|
ArgumentCaptor<SearchRequest> requestCaptor = ArgumentCaptor.forClass(SearchRequest.class);
|
||||||
PlainActionFuture<SearchResponse> searchFuture = PlainActionFuture.newFuture();
|
PlainActionFuture<SearchResponse> searchFuture = PlainActionFuture.newFuture();
|
||||||
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), "", 1, 1, 0, 1234,
|
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), "", 1, 1, 0, 1234,
|
||||||
ShardSearchFailure.EMPTY_ARRAY);
|
ShardSearchFailure.EMPTY_ARRAY, SearchResponse.Clusters.EMPTY);
|
||||||
searchFuture.onResponse(searchResponse);
|
searchFuture.onResponse(searchResponse);
|
||||||
when(client.search(requestCaptor.capture())).thenReturn(searchFuture);
|
when(client.search(requestCaptor.capture())).thenReturn(searchFuture);
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ public class SearchInputTests extends ESTestCase {
|
||||||
ArgumentCaptor<SearchRequest> requestCaptor = ArgumentCaptor.forClass(SearchRequest.class);
|
ArgumentCaptor<SearchRequest> requestCaptor = ArgumentCaptor.forClass(SearchRequest.class);
|
||||||
PlainActionFuture<SearchResponse> searchFuture = PlainActionFuture.newFuture();
|
PlainActionFuture<SearchResponse> searchFuture = PlainActionFuture.newFuture();
|
||||||
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), "", 1, 1, 0, 1234,
|
SearchResponse searchResponse = new SearchResponse(InternalSearchResponse.empty(), "", 1, 1, 0, 1234,
|
||||||
ShardSearchFailure.EMPTY_ARRAY);
|
ShardSearchFailure.EMPTY_ARRAY, SearchResponse.Clusters.EMPTY);
|
||||||
searchFuture.onResponse(searchResponse);
|
searchFuture.onResponse(searchResponse);
|
||||||
when(client.search(requestCaptor.capture())).thenReturn(searchFuture);
|
when(client.search(requestCaptor.capture())).thenReturn(searchFuture);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue