Revert "[8.8] [APM] Circuit breaker and perf improvements for service… (#160132)

This commit is contained in:
Dario Gieselaar 2023-06-21 18:28:09 +02:00 committed by GitHub
parent 258f83fd90
commit f190fd1da9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 162 additions and 204 deletions

View file

@ -1,64 +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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ApmFields, httpExitSpan } from '@kbn/apm-synthtrace-client';
import { service } from '@kbn/apm-synthtrace-client/src/lib/apm/service';
import { Transaction } from '@kbn/apm-synthtrace-client/src/lib/apm/transaction';
import { Scenario } from '../cli/scenario';
import { RunOptions } from '../cli/utils/parse_run_cli_flags';
import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment';
const environment = getSynthtraceEnvironment(__filename);
const scenario: Scenario<ApmFields> = async (runOptions: RunOptions) => {
const numServices = 500;
const tracesPerMinute = 10;
return {
generate: ({ range }) => {
const services = new Array(numServices)
.fill(undefined)
.map((_, idx) => {
return service(`service-${idx}`, 'prod', environment).instance('service-instance');
})
.reverse();
return range.ratePerMinute(tracesPerMinute).generator((timestamp) => {
const rootTransaction = services.reduce((prev, currentService) => {
const tx = currentService
.transaction(`GET /my/function`, 'request')
.timestamp(timestamp)
.duration(1000)
.children(
...(prev
? [
currentService
.span(
httpExitSpan({
spanName: `exit-span-${currentService.fields['service.name']}`,
destinationUrl: `http://address-to-exit-span-${currentService.fields['service.name']}`,
})
)
.timestamp(timestamp)
.duration(1000)
.children(prev),
]
: [])
);
return tx;
}, undefined as Transaction | undefined);
return rootTransaction!;
});
},
};
};
export default scenario;

View file

@ -26,8 +26,6 @@ const configSchema = schema.object({
serviceMapTraceIdBucketSize: schema.number({ defaultValue: 65 }),
serviceMapTraceIdGlobalBucketSize: schema.number({ defaultValue: 6 }),
serviceMapMaxTracesPerRequest: schema.number({ defaultValue: 50 }),
serviceMapTerminateAfter: schema.number({ defaultValue: 100_000 }),
serviceMapMaxTraces: schema.number({ defaultValue: 1000 }),
ui: schema.object({
enabled: schema.boolean({ defaultValue: true }),
maxTraceItems: schema.number({ defaultValue: 5000 }),

View file

@ -15,19 +15,12 @@ import {
} from '../../../common/service_map';
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
export async function fetchServicePathsFromTraceIds({
apmEventClient,
traceIds,
start,
end,
terminateAfter,
}: {
apmEventClient: APMEventClient;
traceIds: string[];
start: number;
end: number;
terminateAfter: number;
}) {
export async function fetchServicePathsFromTraceIds(
apmEventClient: APMEventClient,
traceIds: string[],
start: number,
end: number
) {
// make sure there's a range so ES can skip shards
const dayInMs = 24 * 60 * 60 * 1000;
const startRange = start - dayInMs;
@ -37,7 +30,6 @@ export async function fetchServicePathsFromTraceIds({
apm: {
events: [ProcessorEvent.span, ProcessorEvent.transaction],
},
terminate_after: terminateAfter,
body: {
track_total_hits: false,
size: 0,

View file

@ -46,10 +46,8 @@ async function getConnectionData({
end,
serviceGroupKuery,
kuery,
logger,
}: IEnvOptions) {
return withApmSpan('get_service_map_connections', async () => {
logger.debug('Getting trace sample IDs');
const { traceIds } = await getTraceSampleIds({
config,
apmEventClient,
@ -61,8 +59,6 @@ async function getConnectionData({
kuery,
});
logger.debug(`Found ${traceIds.length} traces to inspect`);
const chunks = chunk(traceIds, config.serviceMapMaxTracesPerRequest);
const init = {
@ -74,8 +70,6 @@ async function getConnectionData({
return init;
}
logger.debug(`Executing scripted metric agg (${chunks.length} chunks)`);
const chunkedResponses = await withApmSpan(
'get_service_paths_from_all_trace_ids',
() =>
@ -86,16 +80,12 @@ async function getConnectionData({
traceIds: traceIdsChunk,
start,
end,
terminateAfter: config.serviceMapTerminateAfter,
logger,
})
)
)
);
logger.debug('Received chunk responses');
const mergedResponses = chunkedResponses.reduce((prev, current) => {
return chunkedResponses.reduce((prev, current) => {
return {
connections: prev.connections.concat(current.connections),
discoveredServices: prev.discoveredServices.concat(
@ -103,10 +93,6 @@ async function getConnectionData({
),
};
});
logger.debug('Merged responses');
return mergedResponses;
});
}
@ -133,19 +119,12 @@ export function getServiceMap(
getServiceStats(options),
anomaliesPromise,
]);
logger.debug('Received and parsed all responses');
const transformedResponse = transformServiceMapResponses({
return transformServiceMapResponses({
response: {
...connectionData,
services: servicesData,
anomalies,
},
});
logger.debug('Transformed service map response');
return transformedResponse;
});
}

View file

@ -11,9 +11,9 @@ import { Connection, ConnectionNode } from '../../../common/service_map';
function getConnectionsPairs(connections: Connection[]) {
return connections
.map((conn) => {
const source = conn.source['service.name'];
const source = `${conn.source['service.name']}:${conn.source['service.environment']}`;
const destination = conn.destination['service.name']
? conn.destination['service.name']
? `${conn.destination['service.name']}:${conn.destination['service.environment']}`
: conn.destination['span.type'];
return `${source} -> ${destination}`;
})
@ -21,71 +21,139 @@ function getConnectionsPairs(connections: Connection[]) {
}
describe('getConnections', () => {
const paths = [
[
{
'service.name': 'opbeans-ruby',
'agent.name': 'ruby',
},
{
'service.name': 'opbeans-node',
'agent.name': 'nodejs',
},
{
'service.name': 'opbeans-go',
'agent.name': 'go',
},
{
'service.name': 'opbeans-java',
'agent.name': 'java',
},
{
'span.subtype': 'http',
'span.destination.service.resource': '172.18.0.6:3000',
'span.type': 'external',
},
],
[
{
'service.name': 'opbeans-ruby',
'agent.name': 'ruby',
},
{
'service.name': 'opbeans-python',
'agent.name': 'python',
},
{
'span.subtype': 'http',
'span.destination.service.resource': '172.18.0.6:3000',
'span.type': 'external',
},
],
[
{
'service.name': 'opbeans-go',
'agent.name': 'go',
},
{
'service.name': 'opbeans-node',
'agent.name': 'nodejs',
},
],
] as ConnectionNode[][];
describe('with environments defined', () => {
const paths = [
[
{
'service.environment': 'testing',
'service.name': 'opbeans-ruby',
'agent.name': 'ruby',
},
{
'service.environment': null,
'service.name': 'opbeans-node',
'agent.name': 'nodejs',
},
{
'service.environment': 'production',
'service.name': 'opbeans-go',
'agent.name': 'go',
},
{
'service.environment': 'production',
'service.name': 'opbeans-java',
'agent.name': 'java',
},
{
'span.subtype': 'http',
'span.destination.service.resource': '172.18.0.6:3000',
'span.type': 'external',
},
],
[
{
'service.environment': 'testing',
'service.name': 'opbeans-ruby',
'agent.name': 'ruby',
},
{
'service.environment': 'testing',
'service.name': 'opbeans-python',
'agent.name': 'python',
},
{
'span.subtype': 'http',
'span.destination.service.resource': '172.18.0.6:3000',
'span.type': 'external',
},
],
] as ConnectionNode[][];
it('includes all connections', () => {
const connections = getConnections({
paths,
it('includes all connections', () => {
const connections = getConnections({
paths,
});
const connectionsPairs = getConnectionsPairs(connections);
expect(connectionsPairs).toEqual([
'opbeans-ruby:testing -> opbeans-node:null',
'opbeans-node:null -> opbeans-go:production',
'opbeans-go:production -> opbeans-java:production',
'opbeans-java:production -> external',
'opbeans-ruby:testing -> opbeans-python:testing',
'opbeans-python:testing -> external',
]);
});
});
const connectionsPairs = getConnectionsPairs(connections);
expect(connectionsPairs).toEqual([
'opbeans-ruby -> opbeans-node',
'opbeans-node -> opbeans-go',
'opbeans-go -> opbeans-java',
'opbeans-java -> external',
'opbeans-ruby -> opbeans-python',
'opbeans-python -> external',
'opbeans-go -> opbeans-node',
]);
describe('environment is "not defined"', () => {
it('includes all connections', () => {
const environmentNotDefinedPaths = [
[
{
'service.environment': 'production',
'service.name': 'opbeans-go',
'agent.name': 'go',
},
{
'service.environment': 'production',
'service.name': 'opbeans-java',
'agent.name': 'java',
},
{
'span.subtype': 'http',
'span.destination.service.resource': '172.18.0.6:3000',
'span.type': 'external',
},
],
[
{
'service.environment': null,
'service.name': 'opbeans-go',
'agent.name': 'go',
},
{
'service.environment': null,
'service.name': 'opbeans-java',
'agent.name': 'java',
},
{
'span.subtype': 'http',
'span.destination.service.resource': '172.18.0.6:3000',
'span.type': 'external',
},
],
[
{
'service.environment': null,
'service.name': 'opbeans-python',
'agent.name': 'python',
},
{
'service.environment': null,
'service.name': 'opbeans-node',
'agent.name': 'nodejs',
},
{
'span.subtype': 'http',
'span.destination.service.resource': '172.18.0.6:3000',
'span.type': 'external',
},
],
] as ConnectionNode[][];
const connections = getConnections({
paths: environmentNotDefinedPaths,
});
const connectionsPairs = getConnectionsPairs(connections);
expect(connectionsPairs).toEqual([
'opbeans-go:production -> opbeans-java:production',
'opbeans-java:production -> external',
'opbeans-go:null -> opbeans-java:null',
'opbeans-java:null -> external',
'opbeans-python:null -> opbeans-node:null',
'opbeans-node:null -> external',
]);
});
});
});

View file

@ -5,43 +5,38 @@
* 2.0.
*/
import { Logger } from '@kbn/logging';
import { find, uniqBy } from 'lodash';
import { Connection, ConnectionNode } from '../../../common/service_map';
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
import { fetchServicePathsFromTraceIds } from './fetch_service_paths_from_trace_ids';
import { getConnectionId } from './transform_service_map_responses';
export function getConnections({
paths,
}: {
paths: ConnectionNode[][] | undefined;
}): Connection[] {
}) {
if (!paths) {
return [];
}
const connectionsById: Map<string, Connection> = new Map();
paths.forEach((path) => {
path.forEach((location, i) => {
const prev = path[i - 1];
const connectionsArr = paths.flatMap((path) => {
return path.reduce((conns, location, index) => {
const prev = path[index - 1];
if (prev) {
const connection = {
return conns.concat({
source: prev,
destination: location,
};
const id = getConnectionId(connection);
if (!connectionsById.has(id)) {
connectionsById.set(id, connection);
}
});
}
});
});
return conns;
}, [] as Connection[]);
}, [] as Connection[]);
return Array.from(connectionsById.values());
const connections = uniqBy(connectionsArr, (value) =>
find(connectionsArr, value)
);
return connections;
}
export async function getServiceMapFromTraceIds({
@ -49,26 +44,14 @@ export async function getServiceMapFromTraceIds({
traceIds,
start,
end,
terminateAfter,
logger,
}: {
apmEventClient: APMEventClient;
traceIds: string[];
start: number;
end: number;
terminateAfter: number;
logger: Logger;
}) {
const serviceMapFromTraceIdsScriptResponse =
await fetchServicePathsFromTraceIds({
apmEventClient,
traceIds,
start,
end,
terminateAfter,
});
logger.debug('Received scripted metric agg response');
await fetchServicePathsFromTraceIds(apmEventClient, traceIds, start, end);
const serviceMapScriptedAggValue =
serviceMapFromTraceIdsScriptResponse.aggregations?.service_map.value;

View file

@ -26,6 +26,8 @@ import { environmentQuery } from '../../../common/utils/environment_query';
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
import { APMConfig } from '../..';
const MAX_TRACES_TO_INSPECT = 1000;
export async function getTraceSampleIds({
serviceName,
environment,
@ -161,7 +163,7 @@ export async function getTraceSampleIds({
uniq(
sortBy(traceIdsWithPriority, 'priority').map(({ traceId }) => traceId)
),
config.serviceMapMaxTraces
MAX_TRACES_TO_INSPECT
);
return { traceIds };

View file

@ -101,7 +101,7 @@ const serviceMapRoute = createApmServerRoute({
serviceName,
environment,
searchAggregatedTransactions,
logger: logger.get('serviceMap'),
logger,
start,
end,
maxNumberOfServices,

View file

@ -35,7 +35,7 @@ function getConnectionNodeId(node: ConnectionNode): string {
return node[SERVICE_NAME];
}
export function getConnectionId(connection: Connection) {
function getConnectionId(connection: Connection) {
return `${getConnectionNodeId(connection.source)}~${getConnectionNodeId(
connection.destination
)}`;