Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Infra Monitoring UI] Add event module filter to metrics table #133872

Merged

Conversation

crespocarlos
Copy link
Contributor

Summary

this PR fixes #131308 by adding event.module filter to metrics table queries. For hosts it will filter by system and for pods and containers by kubernetes. This will filter out some undesired rows without relevant metric data.

Example

Without the filter:
image

With filter:
image

Checklist

For maintainers

@crespocarlos crespocarlos added release_note:fix Feature:Metrics UI Metrics UI feature Team:Infra Monitoring UI - DEPRECATED DEPRECATED - Label for the Infra Monitoring UI team. Use Team:obs-ux-infra_services backport:skip This commit does not require backporting v8.4.0 labels Jun 8, 2022
@crespocarlos crespocarlos changed the title add event module filter to metrics table [Metrics UI] add event module filter to metrics table Jun 8, 2022
@crespocarlos crespocarlos changed the title [Metrics UI] add event module filter to metrics table [Metrics UI] Add event module filter to metrics table Jun 8, 2022
@crespocarlos
Copy link
Contributor Author

@elasticmachine merge upstream

@crespocarlos
Copy link
Contributor Author

@elasticmachine merge upstream

@crespocarlos crespocarlos marked this pull request as ready for review June 9, 2022 07:04
@crespocarlos crespocarlos requested a review from a team as a code owner June 9, 2022 07:04
@elasticmachine
Copy link
Contributor

Pinging @elastic/infra-monitoring-ui (Team:Infra Monitoring UI)

Copy link
Contributor

@miltonhultgren miltonhultgren left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thanks for adding the tests as well!

I would still want @weltenwort to approve as well on the DSL query we end up with, so we don't risk breaking future usages.

@@ -81,6 +87,25 @@ export function useContainerMetricsTable({
};
}

function addEventModuleFilter(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
function addEventModuleFilter(
function addKubernetesModuleFilter(

I feel this would be more clear about which kind of event we want to filter on?

@crespocarlos
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Member

@weltenwort weltenwort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks indeed for adding the hook unit tests.

Comment on lines 101 to 103
{
...filterClauseDsl,
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When typeof filterClauseDsl === 'undefined', then this would put an empty object into the filter array. How about we avoid adding it in the first place?

Suggested change
{
...filterClauseDsl,
},
...(filterClauseDsl != null ? [filterClauseDsl] : []),

Copy link
Contributor Author

@crespocarlos crespocarlos Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! I'll just change your suggestion for an array to an object

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we're merging into an array? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I see now. At first glance I thought you were adding it to the filter array. You are actually concatenating. Got it

filter: [
{
term: {
'event.module': 'system',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is closely coupled to the knowledge that's encoded in hostMetricsMap above. Would there be a reasonable way to automatically derive this from the map? Or at least co-locate this magic string so it doesn't diverge?

What if, instead of filtering for the module, we filtered for the presence of the metrics fields from hostMetricsMap directly? It would me more parsimonious than introducing another constant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with your concern, and we thought about this but the risk is that other documents populate the same fields and are they then the "same" value or not? See #131308 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid point. But the metric field names are rather specific, so I don't imagine the likelihood would be very high. Or do you have a specific confounding source in mind? Additionally, the confounding source could also erroneously set the module name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the ticket I linked, Carlos has data from the APM agent that has the same fields (not all, but some) for the Host table (not sure if the values match what the system module would report though) but in this case it doesn't report the module "wrong". So I don't know :D I don't have a good answer here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, yes... for now the module filter seems like the simplest workaround. still, could we co-locate that knowledge somehow with the metric field names, @crespocarlos?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure! I'll see how that can be improved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactoring was a slightly bigger than I expected, but I think now the knowledge about how the query will need is centralized

@crespocarlos
Copy link
Contributor Author

@elasticmachine merge upstream

@crespocarlos crespocarlos changed the title [Metrics UI] Add event module filter to metrics table [Infra Monitoring UI] Add event module filter to metrics table Jun 15, 2022
@crespocarlos
Copy link
Contributor Author

buildkite test this

@crespocarlos
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Member

@weltenwort weltenwort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for combining the metrics descriptors 👏 I left a few more questions below.

Comment on lines 65 to 73
const [containerMetricsOptions, setContainerMetricsOptions] = useState<MetricsExplorerOptions>();

useEffect(() => {
if (!containerMetricsOptions) {
const { options } = metricsToApiOptions(containerMetricsQueryConfig, filterClauseDsl);
setContainerMetricsOptions(options);
}
}, [filterClauseDsl, containerMetricsOptions]);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be mutable state? Or could this be computed state (e.g. with useMemo)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, indeed. I think when I first decided to go with useEffect I had dependencies that were mutable. Despite filterClauseDsl being passed via props, it is predefined on the client and never changes. I guess we can go with useMemo here. There is no reason to delay the first render here.

expect(options).toEqual({
aggregation: 'avg',
groupBy: 'test.node.type.groupingField',
filterQuery: JSON.stringify({ bool: { filter: [{ term: { 'event.module': 'test' } }, {}] } }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want this empty object in here, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup. it's outdated

@@ -5,44 +5,59 @@
* 2.0.
*/

import type { MetricsMap } from './metrics_to_api_options';
import type { MetricsExplorerOptions } from '../../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
import { createMetricByFieldLookup, MetricsQueryOptions } from './metrics_to_api_options';
Copy link
Member

@weltenwort weltenwort Jun 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to let the compiler auto-sort and deduplicate these imports (and others in the PR)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was too optmistic that vscode would've added the new import to the existing list

Comment on lines 104 to 105
const fields = Object.keys(metricMap) as T[];
const metrics = Object.values(metricMap) as Array<NodeMetricsExplorerOptionsMetric<T>>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: These assertions could be even safer if we let the compiler derive the types, for example:

Suggested change
const fields = Object.keys(metricMap) as T[];
const metrics = Object.values(metricMap) as Array<NodeMetricsExplorerOptionsMetric<T>>;
const fields = Object.keys(metricMap) as Array<keyof typeof metricMap>;
const metrics = Object.values(metricMap) as ObjectValues<typeof metricMap>;

where ObjectValues<T> is defined in

export type ObjectValues<T> = Array<T[keyof T]>;

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! seems neat


const filterClauseWithEventModuleFilter = {
bool: {
filter: [{ term: { 'event.module': 'kubernetes' } }, { ...filterClauseDsl }],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expected value might be outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it sure is!

'kubernetes.container.memory.usage.bytes': {
aggregation: 'avg',
field: 'kubernetes.container.memory.usage.bytes',
groupByField: 'kubernetes.pod.name',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this incorrectly mix the metrics of multiple containers since a pod might have multiple containers? Would kubernetes.container.id be more suitable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. It should indeed be container.id.

Copy link
Contributor

@miltonhultgren miltonhultgren left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍🏼

Comment on lines +97 to +101
return {
bool: {
filter: !!filterClauseDsl ? [sourceFilter, filterClauseDsl] : [sourceFilter],
},
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return {
bool: {
filter: !!filterClauseDsl ? [sourceFilter, filterClauseDsl] : [sourceFilter],
},
};
const filter = [sourceFilter];
if (filterClauseDsl) {
filter.push(filterClauseDsl);
}
return {
bool: {
filter,
},
};

NIt: I'm just allergic to ternaries for the sake of readability.

Copy link
Contributor Author

@crespocarlos crespocarlos Jun 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to agree with that. But here it doesn't seem to be harming the readability. But it's my personal opinion. I could apply your suggestion if it's hurting you eyes :)!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One could argue that mutating the list can be considered an eyesore too 😉

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mutation hurts the soul, not the eyes :D
I'm fine either way @crespocarlos, as long as I get to complain about ternaries :)

import { metricsToApiOptions, useInfrastructureNodeMetrics } from '../shared';
import { createMetricByFieldLookup } from '../shared/hooks/metrics_to_api_options';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Maybe export/import this via ../shared instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally!

@crespocarlos
Copy link
Contributor Author

buildkite test this

Copy link
Member

@weltenwort weltenwort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nicely done

import { metricsToApiOptions, useInfrastructureNodeMetrics } from '../shared';
import { createMetricByFieldLookup } from '../shared/hooks/metrics_to_api_options';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiny nit: looks like this could be imported from ../shared like in the sibling hook variants 😇

@crespocarlos
Copy link
Contributor Author

buildkite test this

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
infra 1001.4KB 1002.1KB +726.0B

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@crespocarlos crespocarlos merged commit b401a7d into elastic:main Jun 16, 2022
@crespocarlos crespocarlos deleted the 131308-add-module-filter-to-metrics-table branch June 16, 2022 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Feature:Metrics UI Metrics UI feature release_note:fix Team:Infra Monitoring UI - DEPRECATED DEPRECATED - Label for the Infra Monitoring UI team. Use Team:obs-ux-infra_services v8.4.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Infra Monitoring UI] Add module filter to metrics tables
6 participants