Skip to content

Commit

Permalink
Merge branch 'main' into 172506-obsux-add-feedback-form-to-apm
Browse files Browse the repository at this point in the history
  • Loading branch information
jennypavlova authored Jan 5, 2024
2 parents 0b4d35e + 71db3d9 commit 9537a30
Show file tree
Hide file tree
Showing 13 changed files with 348 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,33 +133,36 @@ describe('findActiveNodes', () => {
title: 'Root',
path: 'root',
},
// Group 1
'[0][0]': {
id: 'group1',
title: 'Group 1',
deepLink: getDeepLink('group1', 'group1'),
path: 'root.group1',
},
'[0][0][0]': {
id: 'group1A',
title: 'Group 1A',
path: 'root.group1.group1A',
},
'[0][0][0][0]': {
id: 'item1',
title: 'Item 1',
deepLink: getDeepLink('item1', 'item1'),
path: 'root.group1.group1A.item1',
deepLink: getDeepLink('item1', 'item1'), // First match
path: 'root.group1.item1',
},
// Group 2
'[0][1]': {
id: 'group2',
title: 'Group 2',
deepLink: getDeepLink('group2', 'group2'),
path: 'root.group2',
},
'[0][1][0]': {
id: 'group2A',
title: 'Group 2A',
path: 'root.group2.group2A',
},
'[0][1][0][0]': {
id: 'item2',
title: 'Item 2',
deepLink: getDeepLink('item1', 'item1'), // Same link as above, should match both
path: 'root.group2.item2',
// Second match --> should come first as it is the longest match of the 2
deepLink: getDeepLink('item1', 'item1'),
path: 'root.group2.group2A.item2',
},
};

Expand All @@ -172,21 +175,21 @@ describe('findActiveNodes', () => {
path: 'root',
},
{
id: 'group1',
title: 'Group 1',
deepLink: getDeepLink('group1', 'group1'),
path: 'root.group1',
id: 'group2',
title: 'Group 2',
deepLink: getDeepLink('group2', 'group2'),
path: 'root.group2',
},
{
id: 'group1A',
title: 'Group 1A',
path: 'root.group1.group1A',
id: 'group2A',
title: 'Group 2A',
path: 'root.group2.group2A',
},
{
id: 'item1',
title: 'Item 1',
id: 'item2',
title: 'Item 2',
deepLink: getDeepLink('item1', 'item1'),
path: 'root.group1.group1A.item1',
path: 'root.group2.group2A.item2',
},
],
[
Expand All @@ -196,15 +199,15 @@ describe('findActiveNodes', () => {
path: 'root',
},
{
id: 'group2',
title: 'Group 2',
path: 'root.group2',
id: 'group1',
title: 'Group 1',
path: 'root.group1',
},
{
id: 'item2',
title: 'Item 2',
id: 'item1',
title: 'Item 1',
deepLink: getDeepLink('item1', 'item1'),
path: 'root.group2.item2',
path: 'root.group1.item1',
},
],
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ export const findActiveNodes = (
matches[length] = [];
}
matches[length].push(key);
// If there are multiple node matches of the same URL path length, we want to order them by
// tree depth, so that the longest match (deepest node) comes first.
matches[length].sort((a, b) => b.length - a.length);
}
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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, Instance } from '@kbn/apm-synthtrace-client';
import { service } from '@kbn/apm-synthtrace-client/src/lib/apm/service';
import { Scenario } from '../cli/scenario';
import { RunOptions } from '../cli/utils/parse_run_cli_flags';
import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment';
import { withClient } from '../lib/utils/with_client';

const ENVIRONMENT = getSynthtraceEnvironment(__filename);
const MAX_DEPENDENCIES = 10000;
const MAX_DEPENDENCIES_PER_SERVICE = 500;
const MAX_SERVICES = 20;

const scenario: Scenario<ApmFields> = async (runOptions: RunOptions) => {
return {
generate: ({ range, clients: { apmEsClient } }) => {
const javaInstances = Array.from({ length: MAX_SERVICES }).map((_, index) =>
service(`opbeans-java-${index}`, ENVIRONMENT, 'java').instance(`java-instance-${index}`)
);

const instanceDependencies = (instance: Instance, startIndex: number) => {
const rate = range.ratePerMinute(60);

return rate.generator((timestamp, index) => {
const currentIndex = index % MAX_DEPENDENCIES_PER_SERVICE;
const destination = (startIndex + currentIndex) % MAX_DEPENDENCIES;

const span = instance
.transaction({ transactionName: 'GET /java' })
.timestamp(timestamp)
.duration(400)
.success()
.children(
instance
.span({
spanName: 'GET apm-*/_search',
spanType: 'db',
spanSubtype: 'elasticsearch',
})
.destination(`elasticsearch/${destination}`)
.timestamp(timestamp)
.duration(200)
.success()
);

return span;
});
};

return withClient(
apmEsClient,
javaInstances.map((instance, index) =>
instanceDependencies(instance, (index * MAX_DEPENDENCIES_PER_SERVICE) % MAX_DEPENDENCIES)
)
);
},
};
};

export default scenario;
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { synthtrace } from '../../synthtrace';
import { opbeans } from '../fixtures/synthtrace/opbeans';
import { checkA11y } from '../support/commands';
import { synthtrace } from '../../../synthtrace';
import { opbeans } from '../../fixtures/synthtrace/opbeans';
import { checkA11y } from '../../support/commands';
import { generateManyDependencies } from './generate_many_dependencies';

const start = '2021-10-10T00:00:00.000Z';
const end = '2021-10-10T00:15:00.000Z';
Expand Down Expand Up @@ -120,3 +121,46 @@ describe('Dependencies', () => {
});
});
});

describe('Dependencies with high volume of data', () => {
before(() => {
synthtrace.index(
generateManyDependencies({
from: new Date(start).getTime(),
to: new Date(end).getTime(),
})
);
});

after(() => {
synthtrace.clean();
});

beforeEach(() => {
cy.loginAsViewerUser();
});

it('shows dependencies inventory page', () => {
cy.visitKibana(
`/app/apm/dependencies/inventory?${new URLSearchParams({
...timeRange,
kuery: 'elasticsearch*',
})}`
);

cy.getByTestSubj('dependenciesTable');
cy.contains('nav', 'Page 1 of 60');
});

it('shows service dependencies', () => {
cy.visitKibana(
`/app/apm/services/synth-java-0/dependencies?${new URLSearchParams({
...timeRange,
})}`
);

cy.getByTestSubj('serviceDependenciesBreakdownChart').get('canvas');
cy.getByTestSubj('dependenciesTable');
cy.contains('nav', 'Page 1 of 100');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 { apm, Instance, timerange } from '@kbn/apm-synthtrace-client';

const MAX_DEPENDENCIES = 10000;
const MAX_DEPENDENCIES_PER_SERVICE = 500;
const MAX_SERVICES = 20;

export function generateManyDependencies({
from,
to,
}: {
from: number;
to: number;
}) {
const instances = Array.from({ length: MAX_SERVICES }).map((_, index) =>
apm
.service({
name: `synth-java-${index}`,
environment: 'production',
agentName: 'java',
})
.instance(`java-instance-${index}`)
);

const instanceDependencies = (instance: Instance, startIndex: number) => {
return Array.from(
timerange(new Date(from), new Date(to))
.interval('1m')
.rate(60)
.generator((timestamp, index) => {
const currentIndex = index % MAX_DEPENDENCIES_PER_SERVICE;
const destination = (startIndex + currentIndex) % MAX_DEPENDENCIES;

const span = instance
.transaction({ transactionName: 'GET /java' })
.timestamp(timestamp)
.duration(400)
.success()
.children(
instance
.span({
spanName: 'GET apm-*/_search',
spanType: 'db',
spanSubtype: 'elasticsearch',
})
.destination(`elasticsearch/${destination}`)
.timestamp(timestamp)
.duration(200)
.success()
);

return span;
})
);
};

return instances.flatMap((instance, index) =>
instanceDependencies(
instance,
(index * MAX_DEPENDENCIES_PER_SERVICE) % MAX_DEPENDENCIES
)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import { MergedServiceDashboard } from '..';
export function EditDashboard({
onRefresh,
currentDashboard,
serviceName,
}: {
onRefresh: () => void;
currentDashboard: MergedServiceDashboard;
serviceName: string;
}) {
const [isModalVisible, setIsModalVisible] = useState(false);
return (
Expand All @@ -37,6 +39,7 @@ export function EditDashboard({
onClose={() => setIsModalVisible(!isModalVisible)}
onRefresh={onRefresh}
currentDashboard={currentDashboard}
serviceName={serviceName}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ export function LinkDashboard({
onRefresh,
emptyButton = false,
serviceDashboards,
serviceName,
}: {
onRefresh: () => void;
emptyButton?: boolean;
serviceDashboards?: MergedServiceDashboard[];
serviceName: string;
}) {
const [isModalVisible, setIsModalVisible] = useState(false);

Expand Down Expand Up @@ -51,6 +53,7 @@ export function LinkDashboard({
onClose={() => setIsModalVisible(false)}
onRefresh={onRefresh}
serviceDashboards={serviceDashboards}
serviceName={serviceName}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { callApmApi } from '../../../../services/rest/create_call_apm_api';
import { useDashboardFetcher } from '../../../../hooks/use_dashboards_fetcher';
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useApmParams } from '../../../../hooks/use_apm_params';
import { SERVICE_NAME } from '../../../../../common/es_fields/apm';
import { fromQuery, toQuery } from '../../../shared/links/url_helpers';
import { MergedServiceDashboard } from '..';
Expand All @@ -38,13 +37,15 @@ interface Props {
onRefresh: () => void;
currentDashboard?: MergedServiceDashboard;
serviceDashboards?: MergedServiceDashboard[];
serviceName: string;
}

export function SaveDashboardModal({
onClose,
onRefresh,
currentDashboard,
serviceDashboards,
serviceName,
}: Props) {
const {
core: { notifications },
Expand All @@ -71,10 +72,6 @@ export function SaveDashboardModal({

const isEditMode = !!currentDashboard?.id;

const {
path: { serviceName },
} = useApmParams('/services/{serviceName}/dashboards');

const reloadCustomDashboards = useCallback(() => {
onRefresh();
}, [onRefresh]);
Expand Down
Loading

0 comments on commit 9537a30

Please sign in to comment.