From 6ddf2af36d343570093691b5f5b3e8ea918ff78a Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Fri, 30 Jul 2021 14:37:20 -0400 Subject: [PATCH] adding throughput --- .../app/transaction_overview/index.tsx | 19 +-- .../charts/transaction_charts/index.tsx | 37 +---- ...se_transaction_throughput_chart_fetcher.ts | 63 -------- .../throughput_chart_selectors.test.ts | 81 ---------- .../selectors/throughput_chart_selectors.ts | 84 ----------- .../get_throughput_charts/index.ts | 140 ------------------ .../get_throughput_charts/transform.ts | 55 ------- .../plugins/apm/server/routes/transactions.ts | 52 +------ .../tests/feature_controls.ts | 14 -- .../test/apm_api_integration/tests/index.ts | 4 - .../tests/transactions/throughput.ts | 71 --------- 11 files changed, 10 insertions(+), 610 deletions(-) delete mode 100644 x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts delete mode 100644 x-pack/plugins/apm/public/selectors/throughput_chart_selectors.test.ts delete mode 100644 x-pack/plugins/apm/public/selectors/throughput_chart_selectors.ts delete mode 100644 x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts delete mode 100644 x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts delete mode 100644 x-pack/test/apm_api_integration/tests/transactions/throughput.ts diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx index ee05b5ef353a9..6813f2a24fcb7 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx @@ -5,13 +5,7 @@ * 2.0. */ -import { - EuiCallOut, - EuiCode, - EuiPanel, - EuiSpacer, - EuiTitle, -} from '@elastic/eui'; +import { EuiCallOut, EuiCode, EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Location } from 'history'; @@ -24,7 +18,6 @@ import { TransactionCharts } from '../../shared/charts/transaction_charts'; import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink'; import { fromQuery, toQuery } from '../../shared/Links/url_helpers'; import { ServiceOverviewTransactionsTable } from '../service_overview/service_overview_transactions_table'; -import { TransactionList } from './transaction_list'; import { useRedirect } from './useRedirect'; import { useTransactionListFetcher } from './use_transaction_list'; @@ -58,10 +51,7 @@ export function TransactionOverview() { // redirect to first transaction type useRedirect(getRedirectLocation({ location, transactionType, urlParams })); - const { - transactionListData, - transactionListStatus, - } = useTransactionListFetcher(); + const { transactionListData } = useTransactionListFetcher(); // TODO: improve urlParams typings. // `serviceName` or `transactionType` will never be undefined here, and this check should not be needed @@ -74,6 +64,7 @@ export function TransactionOverview() { + {/* TODO: check if it should be calculated in the new table */} {!transactionListData.isAggregationAccurate && ( )} - + /> */} @@ -43,22 +27,7 @@ export function TransactionCharts() { - - - - {i18n.translate( - 'xpack.apm.metrics.transactionChart.throughputLabel', - { defaultMessage: 'Throughput' } - )} - - - - + diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts deleted file mode 100644 index 72e469178a100..0000000000000 --- a/x-pack/plugins/apm/public/hooks/use_transaction_throughput_chart_fetcher.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 { useMemo } from 'react'; -import { useFetcher } from './use_fetcher'; -import { useUrlParams } from '../context/url_params_context/use_url_params'; -import { getThroughputChartSelector } from '../selectors/throughput_chart_selectors'; -import { useTheme } from './use_theme'; -import { useApmServiceContext } from '../context/apm_service/use_apm_service_context'; - -export function useTransactionThroughputChartsFetcher() { - const { transactionType, serviceName } = useApmServiceContext(); - const theme = useTheme(); - const { - urlParams: { environment, kuery, start, end, transactionName }, - } = useUrlParams(); - - const { data, error, status } = useFetcher( - (callApmApi) => { - if (transactionType && serviceName && start && end) { - return callApmApi({ - endpoint: - 'GET /api/apm/services/{serviceName}/transactions/charts/throughput', - params: { - path: { serviceName }, - query: { - environment, - kuery, - start, - end, - transactionType, - transactionName, - }, - }, - }); - } - }, - [ - environment, - kuery, - serviceName, - start, - end, - transactionName, - transactionType, - ] - ); - - const memoizedData = useMemo( - () => getThroughputChartSelector({ throughputChart: data, theme }), - [data, theme] - ); - - return { - throughputChartsData: memoizedData, - throughputChartsStatus: status, - throughputChartsError: error, - }; -} diff --git a/x-pack/plugins/apm/public/selectors/throughput_chart_selectors.test.ts b/x-pack/plugins/apm/public/selectors/throughput_chart_selectors.test.ts deleted file mode 100644 index b76b77abaa7bd..0000000000000 --- a/x-pack/plugins/apm/public/selectors/throughput_chart_selectors.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; -import { - getThroughputChartSelector, - ThroughputChartsResponse, -} from './throughput_chart_selectors'; - -const theme = { - eui: { - euiColorVis1: 'green', - euiColorVis2: 'black', - euiColorVis3: 'gray', - euiColorVis4: 'blue', - euiColorVis6: 'red', - euiColorVis8: 'yellow', - euiColorSecondary: 'white', - euiColorDanger: 'purple', - }, -} as EuiTheme; - -const throughputData = { - throughputTimeseries: [ - { key: 'HTTP 2xx', avg: 1, dataPoints: [{ x: 1, y: 2 }] }, - { key: 'HTTP 4xx', avg: 1, dataPoints: [{ x: 1, y: 2 }] }, - { key: 'HTTP 5xx', avg: 1, dataPoints: [{ x: 1, y: 2 }] }, - ], -} as ThroughputChartsResponse; - -describe('getThroughputChartSelector', () => { - it('returns default values when data is undefined', () => { - const throughputTimeseries = getThroughputChartSelector({ theme }); - expect(throughputTimeseries).toEqual({ throughputTimeseries: [] }); - }); - - it('returns default values when timeseries is empty', () => { - const throughputTimeseries = getThroughputChartSelector({ - theme, - throughputChart: { throughputTimeseries: [] }, - }); - expect(throughputTimeseries).toEqual({ throughputTimeseries: [] }); - }); - - it('return throughput time series', () => { - const throughputTimeseries = getThroughputChartSelector({ - theme, - throughputChart: throughputData, - }); - - expect(throughputTimeseries).toEqual({ - throughputTimeseries: [ - { - title: 'HTTP 2xx', - data: [{ x: 1, y: 2 }], - legendValue: '1.0 tpm', - type: 'linemark', - color: '#327a42', - }, - { - title: 'HTTP 4xx', - data: [{ x: 1, y: 2 }], - legendValue: '1.0 tpm', - type: 'linemark', - color: '#f5a700', - }, - { - title: 'HTTP 5xx', - data: [{ x: 1, y: 2 }], - legendValue: '1.0 tpm', - type: 'linemark', - color: '#c23c2b', - }, - ], - }); - }); -}); diff --git a/x-pack/plugins/apm/public/selectors/throughput_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/throughput_chart_selectors.ts deleted file mode 100644 index f334212536778..0000000000000 --- a/x-pack/plugins/apm/public/selectors/throughput_chart_selectors.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 { difference, zipObject } from 'lodash'; -import { EuiTheme } from '../../../../../src/plugins/kibana_react/common'; -import { asTransactionRate } from '../../common/utils/formatters'; -import { Coordinate, TimeSeries } from '../../typings/timeseries'; -import { APIReturnType } from '../services/rest/createCallApmApi'; -import { httpStatusCodeToColor } from '../utils/httpStatusCodeToColor'; - -export type ThroughputChartsResponse = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/throughput'>; - -export interface ThroughputChart { - throughputTimeseries: Array>; -} - -export function getThroughputChartSelector({ - theme, - throughputChart, -}: { - theme: EuiTheme; - throughputChart?: ThroughputChartsResponse; -}): ThroughputChart { - if (!throughputChart) { - return { throughputTimeseries: [] }; - } - - return { - throughputTimeseries: getThroughputTimeseries({ throughputChart, theme }), - }; -} - -function getThroughputTimeseries({ - throughputChart, - theme, -}: { - theme: EuiTheme; - throughputChart: ThroughputChartsResponse; -}) { - const { throughputTimeseries } = throughputChart; - const bucketKeys = throughputTimeseries.map(({ key }) => key); - const getColor = getColorByKey(bucketKeys, theme); - - return throughputTimeseries.map((bucket) => { - return { - title: bucket.key, - data: bucket.dataPoints, - legendValue: asTransactionRate(bucket.avg), - type: 'linemark', - color: getColor(bucket.key), - }; - }); -} - -function colorMatch(key: string, theme: EuiTheme) { - if (/ok|success/i.test(key)) { - return theme.eui.euiColorSecondary; - } else if (/error|fail/i.test(key)) { - return theme.eui.euiColorDanger; - } -} - -function getColorByKey(keys: string[], theme: EuiTheme) { - const assignedColors = ['HTTP 2xx', 'HTTP 3xx', 'HTTP 4xx', 'HTTP 5xx']; - - const unknownKeys = difference(keys, assignedColors); - const unassignedColors: Record = zipObject(unknownKeys, [ - theme.eui.euiColorVis1, - theme.eui.euiColorVis3, - theme.eui.euiColorVis4, - theme.eui.euiColorVis6, - theme.eui.euiColorVis2, - theme.eui.euiColorVis8, - ]); - - return (key: string) => - colorMatch(key, theme) || - httpStatusCodeToColor(key) || - unassignedColors[key]; -} diff --git a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts deleted file mode 100644 index d5fff20496280..0000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/index.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 { ESFilter } from '../../../../../../../src/core/types/elasticsearch'; -import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { - SERVICE_NAME, - TRANSACTION_NAME, - TRANSACTION_RESULT, - TRANSACTION_TYPE, -} from '../../../../common/elasticsearch_fieldnames'; -import { kqlQuery, rangeQuery } from '../../../../../observability/server'; -import { environmentQuery } from '../../../../common/utils/environment_query'; -import { - getDocumentTypeFilterForAggregatedTransactions, - getProcessorEventForAggregatedTransactions, -} from '../../../lib/helpers/aggregated_transactions'; -import { Setup, SetupTimeRange } from '../../../lib/helpers/setup_request'; -import { getBucketSizeForAggregatedTransactions } from '../../helpers/get_bucket_size_for_aggregated_transactions'; -import { getThroughputBuckets } from './transform'; - -export type ThroughputChartsResponse = PromiseReturnType< - typeof searchThroughput ->; - -function searchThroughput({ - environment, - kuery, - serviceName, - transactionType, - transactionName, - setup, - searchAggregatedTransactions, - intervalString, -}: { - environment?: string; - kuery?: string; - serviceName: string; - transactionType: string; - transactionName: string | undefined; - setup: Setup & SetupTimeRange; - searchAggregatedTransactions: boolean; - intervalString: string; -}) { - const { start, end, apmEventClient } = setup; - - const filter: ESFilter[] = [ - { term: { [SERVICE_NAME]: serviceName } }, - { term: { [TRANSACTION_TYPE]: transactionType } }, - ...getDocumentTypeFilterForAggregatedTransactions( - searchAggregatedTransactions - ), - ...rangeQuery(start, end), - ...environmentQuery(environment), - ...kqlQuery(kuery), - ]; - - if (transactionName) { - filter.push({ term: { [TRANSACTION_NAME]: transactionName } }); - } - - const params = { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { bool: { filter } }, - aggs: { - throughput: { - terms: { field: TRANSACTION_RESULT, missing: '' }, - aggs: { - timeseries: { - date_histogram: { - field: '@timestamp', - fixed_interval: intervalString, - min_doc_count: 0, - extended_bounds: { min: start, max: end }, - }, - }, - }, - }, - }, - }, - }; - - return apmEventClient.search('get_transaction_throughput_series', params); -} - -export async function getThroughputCharts({ - environment, - kuery, - serviceName, - transactionType, - transactionName, - setup, - searchAggregatedTransactions, -}: { - environment?: string; - kuery?: string; - serviceName: string; - transactionType: string; - transactionName: string | undefined; - setup: Setup & SetupTimeRange; - searchAggregatedTransactions: boolean; -}) { - const { bucketSize, intervalString } = getBucketSizeForAggregatedTransactions( - { - ...setup, - searchAggregatedTransactions, - } - ); - - const response = await searchThroughput({ - environment, - kuery, - serviceName, - transactionType, - transactionName, - setup, - searchAggregatedTransactions, - intervalString, - }); - - return { - throughputTimeseries: getThroughputBuckets({ - throughputResultBuckets: response.aggregations?.throughput.buckets, - bucketSize, - setupTimeRange: setup, - }), - }; -} diff --git a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts b/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts deleted file mode 100644 index 1f26e65c460e5..0000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/get_throughput_charts/transform.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 { sortBy } from 'lodash'; -import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; -import { ThroughputChartsResponse } from '.'; -import { calculateThroughput } from '../../helpers/calculate_throughput'; -import { SetupTimeRange } from '../../helpers/setup_request'; - -type ThroughputResultBuckets = Required['aggregations']['throughput']['buckets']; - -export function getThroughputBuckets({ - throughputResultBuckets = [], - bucketSize, - setupTimeRange, -}: { - throughputResultBuckets?: ThroughputResultBuckets; - bucketSize: number; - setupTimeRange: SetupTimeRange; -}) { - const { start, end } = setupTimeRange; - const buckets = throughputResultBuckets.map( - ({ key: resultKey, timeseries }) => { - const dataPoints = timeseries.buckets.map((bucket) => { - return { - x: bucket.key, - // divide by minutes - y: bucket.doc_count / (bucketSize / 60), - }; - }); - - // Handle empty string result keys - const key = - resultKey === '' ? NOT_AVAILABLE_LABEL : (resultKey as string); - - const docCountTotal = timeseries.buckets - .map((bucket) => bucket.doc_count) - .reduce((a, b) => a + b, 0); - - // calculate average throughput - const avg = calculateThroughput({ start, end, value: docCountTotal }); - - return { key, dataPoints, avg }; - } - ); - - return sortBy( - buckets, - (bucket) => bucket.key.toString().replace(/^HTTP (\d)xx$/, '00$1') // ensure that HTTP 3xx are sorted at the top - ); -} diff --git a/x-pack/plugins/apm/server/routes/transactions.ts b/x-pack/plugins/apm/server/routes/transactions.ts index c20de31847e8a..0cd8289ffedbd 100644 --- a/x-pack/plugins/apm/server/routes/transactions.ts +++ b/x-pack/plugins/apm/server/routes/transactions.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { jsonRt } from '@kbn/io-ts-utils'; +import { jsonRt, toNumberRt } from '@kbn/io-ts-utils'; import * as t from 'io-ts'; -import { toNumberRt } from '@kbn/io-ts-utils'; import { LatencyAggregationType, latencyAggregationTypeRt, @@ -20,7 +19,6 @@ import { getTransactionBreakdown } from '../lib/transactions/breakdown'; import { getTransactionDistribution } from '../lib/transactions/distribution'; import { getAnomalySeries } from '../lib/transactions/get_anomaly_data'; import { getLatencyPeriods } from '../lib/transactions/get_latency_charts'; -import { getThroughputCharts } from '../lib/transactions/get_throughput_charts'; import { getTransactionGroupList } from '../lib/transaction_groups'; import { getErrorRatePeriods } from '../lib/transaction_groups/get_error_rate'; import { createApmServerRoute } from './create_apm_server_route'; @@ -28,8 +26,8 @@ import { createApmServerRouteRepository } from './create_apm_server_route_reposi import { comparisonRangeRt, environmentRt, - rangeRt, kueryRt, + rangeRt, } from './default_api_types'; /** @@ -250,51 +248,6 @@ const transactionLatencyChartsRoute = createApmServerRoute({ }, }); -const transactionThroughputChartsRoute = createApmServerRoute({ - endpoint: - 'GET /api/apm/services/{serviceName}/transactions/charts/throughput', - params: t.type({ - path: t.type({ - serviceName: t.string, - }), - query: t.intersection([ - t.type({ transactionType: t.string }), - t.partial({ transactionName: t.string }), - environmentRt, - kueryRt, - rangeRt, - ]), - }), - options: { tags: ['access:apm'] }, - handler: async (resources) => { - const setup = await setupRequest(resources); - const { params } = resources; - - const { serviceName } = params.path; - const { - environment, - kuery, - transactionType, - transactionName, - } = params.query; - - const searchAggregatedTransactions = await getSearchAggregatedTransactions({ - ...setup, - kuery, - }); - - return await getThroughputCharts({ - environment, - kuery, - serviceName, - transactionType, - transactionName, - setup, - searchAggregatedTransactions, - }); - }, -}); - const transactionChartsDistributionRoute = createApmServerRoute({ endpoint: 'GET /api/apm/services/{serviceName}/transactions/charts/distribution', @@ -439,7 +392,6 @@ export const transactionRouteRepository = createApmServerRouteRepository() .add(transactionGroupsMainStatisticsRoute) .add(transactionGroupsDetailedStatisticsRoute) .add(transactionLatencyChartsRoute) - .add(transactionThroughputChartsRoute) .add(transactionChartsDistributionRoute) .add(transactionChartsBreakdownRoute) .add(transactionChartsErrorRateRoute); diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.ts b/x-pack/test/apm_api_integration/tests/feature_controls.ts index 553f22fc2279e..b56cd4b9c6ed8 100644 --- a/x-pack/test/apm_api_integration/tests/feature_controls.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.ts @@ -121,20 +121,6 @@ export default function featureControlsTests({ getService }: FtrProviderContext) expectForbidden: expect403, expectResponse: expect200, }, - { - req: { - url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar`, - }, - expectForbidden: expect403, - expectResponse: expect200, - }, - { - req: { - url: `/api/apm/services/foo/transactions/charts/throughput?environment=testing&start=${start}&end=${end}&transactionType=bar&transactionName=baz`, - }, - expectForbidden: expect403, - expectResponse: expect200, - }, { req: { url: `/api/apm/services/foo/transactions/charts/distribution?start=${start}&end=${end}&transactionType=bar&transactionName=baz`, diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index a00fa1723fa3e..a8748b73b1203 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -166,10 +166,6 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte loadTestFile(require.resolve('./transactions/latency')); }); - describe('transactions/throughput', function () { - loadTestFile(require.resolve('./transactions/throughput')); - }); - describe('transactions/top_transaction_groups', function () { loadTestFile(require.resolve('./transactions/top_transaction_groups')); }); diff --git a/x-pack/test/apm_api_integration/tests/transactions/throughput.ts b/x-pack/test/apm_api_integration/tests/transactions/throughput.ts deleted file mode 100644 index 5c2de185fdf79..0000000000000 --- a/x-pack/test/apm_api_integration/tests/transactions/throughput.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import url from 'url'; -import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; -import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { registry } from '../../common/registry'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - const archiveName = 'apm_8.0.0'; - const metadata = archives_metadata[archiveName]; - - // url parameters - const { start, end } = metadata; - - registry.when('Throughput when data is not loaded', { config: 'basic', archives: [] }, () => { - it('handles the empty state', async () => { - const response = await supertest.get( - url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/throughput`, - query: { - environment: 'testing', - start, - end, - transactionType: 'request', - }, - }) - ); - - expect(response.status).to.be(200); - - expect(response.body.throughputTimeseries.length).to.be(0); - }); - }); - - registry.when( - 'Throughput when data is loaded', - { config: 'basic', archives: [archiveName] }, - () => { - let response: PromiseReturnType; - - before(async () => { - response = await supertest.get( - url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/throughput`, - query: { - environment: 'testing', - start, - end, - transactionType: 'request', - }, - }) - ); - }); - - it('returns throughput timeseries', async () => { - expect(response.status).to.be(200); - - expect(response.body.throughputTimeseries.length).to.be.greaterThan(0); - }); - } - ); -}