[Maps] Add execution context (#123651)

This commit is contained in:
Thomas Neirynck 2022-01-27 13:43:12 -05:00 committed by GitHub
parent 41ffa3e0fa
commit 14d6f7c871
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 117 additions and 36 deletions

View file

@ -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]

View 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,
};
}

View file

@ -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);

View file

@ -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,

View file

@ -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([

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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)) {

View file

@ -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,
}
);
}
);

View file

@ -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,

View file

@ -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 });
}