mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Maps] Add execution context (#123651)
This commit is contained in:
parent
41ffa3e0fa
commit
14d6f7c871
13 changed files with 117 additions and 36 deletions
|
@ -3,7 +3,7 @@
|
|||
Sometimes the {es} server might be slowed down by the execution of an expensive query. Such queries are logged to {es}'s {ref}/index-modules-slowlog.html#search-slow-log[search slow log] file. But there is a problem: it's impossible to say what triggered a slow search request—a {kib} instance or a user accessing an {es} endpoint directly.
|
||||
To simplify the investigation of such cases, the search slow log file includes the `x-opaque-id` header, which might provide additional information about a request if it originated from {kib}.
|
||||
|
||||
WARNING: At the moment, {kib} can only highlight cases where a slow query originated from a {kib} visualization, *Lens*, *Discover*, or *Alerting*.
|
||||
WARNING: At the moment, {kib} can only highlight cases where a slow query originated from a {kib} visualization, *Lens*, *Discover*, *Maps*, or *Alerting*.
|
||||
|
||||
For example, if a request to {es} was initiated by a Vega visualization on a dashboard, you will see the following in the slow logs:
|
||||
[source,json]
|
||||
|
|
18
x-pack/plugins/maps/common/execution_context.ts
Normal file
18
x-pack/plugins/maps/common/execution_context.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { APP_ID } from './constants';
|
||||
|
||||
export function makeExecutionContext(id: string, url: string, description?: string) {
|
||||
return {
|
||||
name: APP_ID,
|
||||
type: 'application',
|
||||
id,
|
||||
description: description || '',
|
||||
url,
|
||||
};
|
||||
}
|
|
@ -44,6 +44,7 @@ import { ISearchSource } from '../../../../../../../src/plugins/data/common/sear
|
|||
import { IndexPattern } from '../../../../../../../src/plugins/data/common';
|
||||
import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters';
|
||||
import { isValidStringConfig } from '../../util/valid_string_config';
|
||||
import { makePublicExecutionContext } from '../../../util';
|
||||
|
||||
type ESGeoGridSourceSyncMeta = Pick<ESGeoGridSourceDescriptor, 'requestType' | 'resolution'>;
|
||||
|
||||
|
@ -287,6 +288,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
}
|
||||
),
|
||||
searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_geo_grid_source:cluster_composite'),
|
||||
});
|
||||
|
||||
features.push(...convertCompositeRespToGeoJson(esResponse, this._descriptor.requestType));
|
||||
|
@ -360,6 +362,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo
|
|||
defaultMessage: 'Elasticsearch geo grid aggregation request',
|
||||
}),
|
||||
searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_geo_grid_source:cluster'),
|
||||
});
|
||||
|
||||
return convertRegularRespToGeoJson(esResponse, this._descriptor.requestType);
|
||||
|
|
|
@ -39,6 +39,7 @@ import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_proper
|
|||
import { esFilters } from '../../../../../../../src/plugins/data/public';
|
||||
import { getIsGoldPlus } from '../../../licensed_features';
|
||||
import { LICENSED_FEATURES } from '../../../licensed_features';
|
||||
import { makePublicExecutionContext } from '../../../util';
|
||||
|
||||
type ESGeoLineSourceSyncMeta = Pick<ESGeoLineSourceDescriptor, 'splitField' | 'sortField'>;
|
||||
|
||||
|
@ -225,6 +226,7 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
defaultMessage: 'Elasticsearch terms request to fetch entities within map buffer.',
|
||||
}),
|
||||
searchSessionId: searchFilters.searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_geo_line:entities'),
|
||||
});
|
||||
const entityBuckets: Array<{ key: string; doc_count: number }> = _.get(
|
||||
entityResp,
|
||||
|
@ -296,6 +298,7 @@ export class ESGeoLineSource extends AbstractESAggSource {
|
|||
'Elasticsearch geo_line request to fetch tracks for entities. Tracks are not filtered by map buffer.',
|
||||
}),
|
||||
searchSessionId: searchFilters.searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_geo_line:tracks'),
|
||||
});
|
||||
const { featureCollection, numTrimmedTracks } = convertToGeoJson(
|
||||
tracksResp,
|
||||
|
|
|
@ -18,6 +18,7 @@ import { AbstractESAggSource } from '../es_agg_source';
|
|||
import { registerSource } from '../source_registry';
|
||||
import { turfBboxToBounds } from '../../../../common/elasticsearch_util';
|
||||
import { DataRequestAbortError } from '../../util/data_request';
|
||||
import { makePublicExecutionContext } from '../../../util';
|
||||
|
||||
const MAX_GEOTILE_LEVEL = 29;
|
||||
|
||||
|
@ -163,6 +164,7 @@ export class ESPewPewSource extends AbstractESAggSource {
|
|||
defaultMessage: 'Source-destination connections request',
|
||||
}),
|
||||
searchSessionId: searchFilters.searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_pew_pew_source:connections'),
|
||||
});
|
||||
|
||||
const { featureCollection } = convertToLines(esResponse);
|
||||
|
@ -202,6 +204,7 @@ export class ESPewPewSource extends AbstractESAggSource {
|
|||
const esResp = await searchSource.fetch({
|
||||
abortSignal: abortController.signal,
|
||||
legacyHitsTotal: false,
|
||||
executionContext: makePublicExecutionContext('es_pew_pew_source:bounds'),
|
||||
});
|
||||
if (esResp.aggregations.destFitToBounds.bounds) {
|
||||
corners.push([
|
||||
|
|
|
@ -72,6 +72,7 @@ import {
|
|||
getIsDrawLayer,
|
||||
getMatchingIndexes,
|
||||
} from './util/feature_edit';
|
||||
import { makePublicExecutionContext } from '../../../util';
|
||||
|
||||
type ESSearchSourceSyncMeta = Pick<
|
||||
ESSearchSourceDescriptor,
|
||||
|
@ -357,6 +358,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
registerCancelCallback,
|
||||
requestDescription: 'Elasticsearch document top hits request',
|
||||
searchSessionId: searchFilters.searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_search_source:top_hits'),
|
||||
});
|
||||
|
||||
const allHits: any[] = [];
|
||||
|
@ -438,6 +440,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
registerCancelCallback,
|
||||
requestDescription: 'Elasticsearch document request',
|
||||
searchSessionId: searchFilters.searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_search_source:doc_search'),
|
||||
});
|
||||
|
||||
const isTimeExtentForTimeslice =
|
||||
|
@ -599,7 +602,10 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
searchSource.setField('query', query);
|
||||
searchSource.setField('fieldsFromSource', this._getTooltipPropertyNames());
|
||||
|
||||
const resp = await searchSource.fetch({ legacyHitsTotal: false });
|
||||
const resp = await searchSource.fetch({
|
||||
legacyHitsTotal: false,
|
||||
executionContext: makePublicExecutionContext('es_search_source:load_tooltip_properties'),
|
||||
});
|
||||
|
||||
const hit = _.get(resp, 'hits.hits[0]');
|
||||
if (!hit) {
|
||||
|
@ -905,6 +911,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource
|
|||
abortSignal: abortController.signal,
|
||||
sessionId: searchFilters.searchSessionId,
|
||||
legacyHitsTotal: false,
|
||||
executionContext: makePublicExecutionContext('es_search_source:all_doc_counts'),
|
||||
});
|
||||
return !isTotalHitsGreaterThan(resp.hits.total as unknown as TotalHits, maxResultWindow);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import uuid from 'uuid/v4';
|
|||
import { Filter } from '@kbn/es-query';
|
||||
import { IndexPatternField, IndexPattern, ISearchSource } from 'src/plugins/data/public';
|
||||
import type { Query } from 'src/plugins/data/common';
|
||||
import type { KibanaExecutionContext } from 'kibana/public';
|
||||
import { AbstractVectorSource, BoundsRequestMeta } from '../vector_source';
|
||||
import {
|
||||
getAutocompleteService,
|
||||
|
@ -38,6 +39,7 @@ import { IField } from '../../fields/field';
|
|||
import { FieldFormatter } from '../../../../common/constants';
|
||||
import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters';
|
||||
import { isValidStringConfig } from '../../util/valid_string_config';
|
||||
import { makePublicExecutionContext } from '../../../util';
|
||||
|
||||
export function isSearchSourceAbortError(error: Error) {
|
||||
return error.name === 'AbortError';
|
||||
|
@ -160,6 +162,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
requestName,
|
||||
searchSessionId,
|
||||
searchSource,
|
||||
executionContext,
|
||||
}: {
|
||||
registerCancelCallback: (callback: () => void) => void;
|
||||
requestDescription: string;
|
||||
|
@ -167,6 +170,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
requestName: string;
|
||||
searchSessionId?: string;
|
||||
searchSource: ISearchSource;
|
||||
executionContext: KibanaExecutionContext;
|
||||
}): Promise<any> {
|
||||
const abortController = new AbortController();
|
||||
registerCancelCallback(() => abortController.abort());
|
||||
|
@ -183,6 +187,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
title: requestName,
|
||||
description: requestDescription,
|
||||
},
|
||||
executionContext,
|
||||
})
|
||||
.toPromise();
|
||||
return resp;
|
||||
|
@ -277,6 +282,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
const esResp = await searchSource.fetch({
|
||||
abortSignal: abortController.signal,
|
||||
legacyHitsTotal: false,
|
||||
executionContext: makePublicExecutionContext('es_source:bounds'),
|
||||
});
|
||||
|
||||
if (!esResp.aggregations) {
|
||||
|
@ -469,6 +475,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
}
|
||||
),
|
||||
searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_source:style_meta'),
|
||||
});
|
||||
|
||||
return resp.aggregations;
|
||||
|
|
|
@ -32,6 +32,7 @@ import { PropertiesMap } from '../../../../common/elasticsearch_util';
|
|||
import { isValidStringConfig } from '../../util/valid_string_config';
|
||||
import { ITermJoinSource } from '../term_join_source';
|
||||
import { IField } from '../../fields/field';
|
||||
import { makePublicExecutionContext } from '../../../util';
|
||||
|
||||
const TERMS_AGG_NAME = 'join';
|
||||
const TERMS_BUCKET_KEYS_TO_IGNORE = ['key', 'doc_count'];
|
||||
|
@ -153,6 +154,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource
|
|||
},
|
||||
}),
|
||||
searchSessionId: searchFilters.searchSessionId,
|
||||
executionContext: makePublicExecutionContext('es_term_source:terms'),
|
||||
});
|
||||
|
||||
const countPropertyName = this.getAggKey(AGG_TYPE.COUNT);
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
*/
|
||||
|
||||
import { EMSClient, FileLayer, TMSService } from '@elastic/ems-client';
|
||||
import type { KibanaExecutionContext } from 'kibana/public';
|
||||
import { FONTS_API_PATH } from '../common/constants';
|
||||
import { getHttp, getTilemap, getEMSSettings, getMapsEmsStart } from './kibana_services';
|
||||
import { getLicenseId } from './licensed_features';
|
||||
import { makeExecutionContext } from '../common/execution_context';
|
||||
|
||||
export function getKibanaTileMap(): unknown {
|
||||
return getTilemap();
|
||||
|
@ -56,3 +58,10 @@ export function getGlyphUrl(): string {
|
|||
export function isRetina(): boolean {
|
||||
return window.devicePixelRatio === 2;
|
||||
}
|
||||
|
||||
export function makePublicExecutionContext(
|
||||
id: string,
|
||||
description?: string
|
||||
): KibanaExecutionContext {
|
||||
return makeExecutionContext(id, window.location.pathname, description);
|
||||
}
|
||||
|
|
|
@ -5,13 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Logger } from 'src/core/server';
|
||||
import { CoreStart, Logger } from 'src/core/server';
|
||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import { Stream } from 'stream';
|
||||
import { RENDER_AS } from '../../common/constants';
|
||||
import { isAbortError } from './util';
|
||||
import { makeExecutionContext } from '../../common/execution_context';
|
||||
|
||||
export async function getEsGridTile({
|
||||
url,
|
||||
core,
|
||||
logger,
|
||||
context,
|
||||
index,
|
||||
|
@ -24,6 +27,8 @@ export async function getEsGridTile({
|
|||
gridPrecision,
|
||||
abortController,
|
||||
}: {
|
||||
url: string;
|
||||
core: CoreStart;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
|
@ -49,20 +54,27 @@ export async function getEsGridTile({
|
|||
fields: requestBody.fields,
|
||||
runtime_mappings: requestBody.runtime_mappings,
|
||||
};
|
||||
const tile = await context.core.elasticsearch.client.asCurrentUser.transport.request(
|
||||
{
|
||||
method: 'GET',
|
||||
path,
|
||||
body,
|
||||
},
|
||||
{
|
||||
signal: abortController.signal,
|
||||
headers: {
|
||||
'Accept-Encoding': 'gzip',
|
||||
},
|
||||
asStream: true,
|
||||
|
||||
const tile = await core.executionContext.withContext(
|
||||
makeExecutionContext('mvt:get_grid_tile', url),
|
||||
async () => {
|
||||
return await context.core.elasticsearch.client.asCurrentUser.transport.request(
|
||||
{
|
||||
method: 'GET',
|
||||
path,
|
||||
body,
|
||||
},
|
||||
{
|
||||
signal: abortController.signal,
|
||||
headers: {
|
||||
'Accept-Encoding': 'gzip',
|
||||
},
|
||||
asStream: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return tile.body as Stream;
|
||||
} catch (e) {
|
||||
if (!isAbortError(e)) {
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { Logger } from 'src/core/server';
|
||||
import { CoreStart, Logger } from 'src/core/server';
|
||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import { Stream } from 'stream';
|
||||
import { isAbortError } from './util';
|
||||
import { makeExecutionContext } from '../../common/execution_context';
|
||||
|
||||
export async function getEsTile({
|
||||
url,
|
||||
core,
|
||||
logger,
|
||||
context,
|
||||
index,
|
||||
|
@ -22,6 +25,8 @@ export async function getEsTile({
|
|||
requestBody = {},
|
||||
abortController,
|
||||
}: {
|
||||
url: string;
|
||||
core: CoreStart;
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
|
@ -45,18 +50,24 @@ export async function getEsTile({
|
|||
runtime_mappings: requestBody.runtime_mappings,
|
||||
track_total_hits: requestBody.size + 1,
|
||||
};
|
||||
const tile = await context.core.elasticsearch.client.asCurrentUser.transport.request(
|
||||
{
|
||||
method: 'GET',
|
||||
path,
|
||||
body,
|
||||
},
|
||||
{
|
||||
signal: abortController.signal,
|
||||
headers: {
|
||||
'Accept-Encoding': 'gzip',
|
||||
},
|
||||
asStream: true,
|
||||
|
||||
const tile = await core.executionContext.withContext(
|
||||
makeExecutionContext('mvt:get_tile', url),
|
||||
async () => {
|
||||
return await context.core.elasticsearch.client.asCurrentUser.transport.request(
|
||||
{
|
||||
method: 'GET',
|
||||
path,
|
||||
body,
|
||||
},
|
||||
{
|
||||
signal: abortController.signal,
|
||||
headers: {
|
||||
'Accept-Encoding': 'gzip',
|
||||
},
|
||||
asStream: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import rison from 'rison-node';
|
||||
import { Stream } from 'stream';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { KibanaRequest, KibanaResponseFactory, Logger } from 'src/core/server';
|
||||
import { CoreStart, KibanaRequest, KibanaResponseFactory, Logger } from 'src/core/server';
|
||||
import { IRouter } from 'src/core/server';
|
||||
import type { DataRequestHandlerContext } from 'src/plugins/data/server';
|
||||
import {
|
||||
|
@ -25,9 +25,11 @@ const CACHE_TIMEOUT_SECONDS = 60 * 60;
|
|||
export function initMVTRoutes({
|
||||
router,
|
||||
logger,
|
||||
core,
|
||||
}: {
|
||||
router: IRouter<DataRequestHandlerContext>;
|
||||
logger: Logger;
|
||||
core: CoreStart;
|
||||
}) {
|
||||
router.get(
|
||||
{
|
||||
|
@ -58,6 +60,8 @@ export function initMVTRoutes({
|
|||
const requestBodyDSL = rison.decode(query.requestBody as string);
|
||||
|
||||
const gzippedTile = await getEsTile({
|
||||
url: `${API_ROOT_PATH}/${MVT_GETTILE_API_PATH}/{z}/{x}/{y}.pbf`,
|
||||
core,
|
||||
logger,
|
||||
context,
|
||||
geometryFieldName: query.geometryFieldName as string,
|
||||
|
@ -104,6 +108,8 @@ export function initMVTRoutes({
|
|||
const requestBodyDSL = rison.decode(query.requestBody as string);
|
||||
|
||||
const gzipTileStream = await getEsGridTile({
|
||||
url: `${API_ROOT_PATH}/${MVT_GETGRIDTILE_API_PATH}/{z}/{x}/{y}.pbf`,
|
||||
core,
|
||||
logger,
|
||||
context,
|
||||
geometryFieldName: query.geometryFieldName as string,
|
||||
|
|
|
@ -8,18 +8,18 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { CoreSetup, IRouter, Logger } from 'kibana/server';
|
||||
import { CoreSetup, CoreStart, IRouter, Logger } from 'kibana/server';
|
||||
import { INDEX_SETTINGS_API_PATH, FONTS_API_PATH } from '../common/constants';
|
||||
import { getIndexPatternSettings } from './lib/get_index_pattern_settings';
|
||||
import { initMVTRoutes } from './mvt/mvt_routes';
|
||||
import { initIndexingRoutes } from './data_indexing/indexing_routes';
|
||||
import { StartDeps, SetupDeps } from './types';
|
||||
import { StartDeps } from './types';
|
||||
import { DataRequestHandlerContext } from '../../../../src/plugins/data/server';
|
||||
|
||||
export async function initRoutes(core: CoreSetup, logger: Logger): Promise<void> {
|
||||
const router: IRouter<DataRequestHandlerContext> = core.http.createRouter();
|
||||
const [, { data: dataPlugin }]: [SetupDeps, StartDeps] =
|
||||
(await core.getStartServices()) as unknown as [SetupDeps, StartDeps];
|
||||
export async function initRoutes(coreSetup: CoreSetup, logger: Logger): Promise<void> {
|
||||
const router: IRouter<DataRequestHandlerContext> = coreSetup.http.createRouter();
|
||||
const [coreStart, { data: dataPlugin }]: [CoreStart, StartDeps] =
|
||||
(await coreSetup.getStartServices()) as unknown as [CoreStart, StartDeps];
|
||||
|
||||
router.get(
|
||||
{
|
||||
|
@ -94,6 +94,6 @@ export async function initRoutes(core: CoreSetup, logger: Logger): Promise<void>
|
|||
}
|
||||
);
|
||||
|
||||
initMVTRoutes({ router, logger });
|
||||
initMVTRoutes({ router, logger, core: coreStart });
|
||||
initIndexingRoutes({ router, logger, dataPlugin });
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue