From 4ae945d021fbda8fc0d6e4a1d29cc2257c67980b Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Wed, 30 Oct 2024 18:30:16 +0100 Subject: [PATCH] [ML] Fix overall bucket request for large number of job IDs (#198292) ## Summary Fixes https://github.com/elastic/kibana/issues/190591 Adds chunking for `over_buckets` request to prevent exceeding the request length URL. ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios (cherry picked from commit fa0f397a4269a37370d2101a8c5ece03ad0117bd) --- .../services/ml_api_service/index.ts | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index f69e60453bfd4..f21e67fe450f4 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -11,7 +11,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { RuntimeMappings } from '@kbn/ml-runtime-field-utils'; -import { isNumber } from 'lodash'; +import { chunk, isNumber } from 'lodash'; import { ML_INTERNAL_BASE_PATH } from '../../../../common/constants/app'; import type { MlServerDefaults, @@ -397,13 +397,13 @@ export function mlApiProvider(httpService: HttpService) { end, overallScore, }: { - jobId: string; + jobId: string[]; topN: string; bucketSpan: string; start: number; end: number; overallScore?: number; - }) { + }): Promise { const body = JSON.stringify({ topN, bucketSpan, @@ -411,11 +411,31 @@ export function mlApiProvider(httpService: HttpService) { end, ...(overallScore ? { overall_score: overallScore } : {}), }); - return httpService.http({ - path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/${jobId}/results/overall_buckets`, - method: 'POST', - body, - version: '1', + + // Max permitted job_id is 64 characters, so we can fit around 30 jobs per request + const maxJobsPerRequest = 30; + + return Promise.all( + chunk(jobId, maxJobsPerRequest).map((jobIdsChunk) => { + return httpService.http({ + path: `${ML_INTERNAL_BASE_PATH}/anomaly_detectors/${jobIdsChunk.join( + ',' + )}/results/overall_buckets`, + method: 'POST', + body, + version: '1', + }); + }) + ).then((responses) => { + // Merge responses + return responses.reduce( + (acc, response) => { + acc.count += response.count; + acc.overall_buckets.push(...response.overall_buckets); + return acc; + }, + { count: 0, overall_buckets: [] } + ); }); },