Skip to content

Commit 9cf8949

Browse files
[7.x][ML] DFA _explain API should not fail when none field is included (#66281) (#66288)
This commit fixes an issue with DFA _explain API where if it is called and no field is included, it results to an error message coming from the c++ process due to the data frame having no columns. We want the _explain API not to error when no fields are included exactly in order to explain to the user why it is that no fields are included. Thus, we can simply fix this by not running the memory estimation process and returning zero estimates instead. Note that the _start API will fail with a user friendly error message that informs there are no included fields. Backport of #66281
1 parent 9fe6f93 commit 9cf8949

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportExplainDataFrameAnalyticsAction.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.elasticsearch.common.collect.Tuple;
1818
import org.elasticsearch.common.inject.Inject;
1919
import org.elasticsearch.common.settings.Settings;
20+
import org.elasticsearch.common.unit.ByteSizeValue;
2021
import org.elasticsearch.license.LicenseUtils;
2122
import org.elasticsearch.license.XPackLicenseState;
2223
import org.elasticsearch.tasks.Task;
@@ -106,7 +107,7 @@ private void explain(Task task, PutDataFrameAnalyticsAction.Request request,
106107
);
107108
if (licenseState.isSecurityEnabled()) {
108109
useSecondaryAuthIfAvailable(this.securityContext, () -> {
109-
// Set the auth headers (preferring the secondary headers) to the caller's.
110+
// Set the auth headers (preferring the secondary headers) to the caller's.
110111
// Regardless if the config was previously stored or not.
111112
DataFrameAnalyticsConfig config = new DataFrameAnalyticsConfig.Builder(request.getConfig())
112113
.setHeaders(filterSecurityHeaders(threadPool.getThreadContext().getHeaders()))
@@ -152,6 +153,11 @@ private void estimateMemoryUsage(Task task,
152153
DataFrameAnalyticsConfig config,
153154
ExtractedFields extractedFields,
154155
ActionListener<MemoryEstimation> listener) {
156+
if (extractedFields.getAllFields().isEmpty()) {
157+
listener.onResponse(new MemoryEstimation(ByteSizeValue.ZERO, ByteSizeValue.ZERO));
158+
return;
159+
}
160+
155161
final String estimateMemoryTaskId = "memory_usage_estimation_" + task.getId();
156162
DataFrameDataExtractorFactory extractorFactory = DataFrameDataExtractorFactory.createForSourceIndices(
157163
new ParentTaskAssigningClient(client, task.getParentTaskId()), estimateMemoryTaskId, config, extractedFields);

x-pack/plugin/src/test/resources/rest-api-spec/test/ml/explain_data_frame_analytics.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,35 @@
312312
- match: { field_selection.4.is_required: false }
313313
- match: { field_selection.4.feature_type: "categorical" }
314314
- is_false: field_selection.4.reason
315+
316+
---
317+
"Test given no included field":
318+
319+
- do:
320+
indices.create:
321+
index: index-source
322+
body:
323+
mappings:
324+
properties:
325+
x:
326+
type: keyword
327+
328+
- do:
329+
index:
330+
index: index-source
331+
refresh: true
332+
body: { x: "hello!" }
333+
- match: { result: "created" }
334+
335+
- do:
336+
ml.explain_data_frame_analytics:
337+
body:
338+
source: { index: "index-source" }
339+
analysis: { outlier_detection: {} }
340+
- match:
341+
memory_estimation.expected_memory_without_disk: "0"
342+
- match:
343+
memory_estimation.expected_memory_with_disk: "0"
344+
- length: { field_selection: 1 }
345+
- match: { field_selection.0.name: "x" }
346+
- match: { field_selection.0.is_included: false }

0 commit comments

Comments
 (0)