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

[ML] Anomaly Detection: Fix anomaly marker click in Single Metric Viewer embeddable #176788

Merged
merged 25 commits into from
Mar 5, 2024

Conversation

walterra
Copy link
Contributor

@walterra walterra commented Feb 13, 2024

Summary

Part of #176651 and #153476. Follow up to #175556.

Fixes the click on anomaly markers in the Single Metric Viewer embeddable. This required refactoring some dependencies to be properly passed in via React contexts instead of the legacy dependency cache.

smv-embeddable-click-0001.webm

Checklist

@walterra walterra added the release_note:skip Skip the PR/issue when compiling release notes label Feb 13, 2024
@walterra walterra self-assigned this Feb 13, 2024
@walterra walterra mentioned this pull request Feb 13, 2024
59 tasks
@walterra walterra marked this pull request as ready for review February 13, 2024 16:06
@walterra walterra requested a review from a team as a code owner February 13, 2024 16:06
@walterra walterra added bug Fixes for quality problems that affect the customer experience :ml Feature:Anomaly Detection ML anomaly detection v8.13.0 labels Feb 13, 2024
@elasticmachine
Copy link
Contributor

Pinging @elastic/ml-ui (:ml)

if (dataViewsService === null) {
throw new Error('Data views are not initialized!');
}
export async function getDataViewIdFromName(
Copy link
Member

Choose a reason for hiding this comment

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

Would it be worth putting these functions in a provider which takes the dataViewsService ?
I see there are a few places where this is called multiple times in the same file.

Copy link
Contributor

@alvarezmelissa87 alvarezmelissa87 Feb 13, 2024

Choose a reason for hiding this comment

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

@walterra, @jgowdyelastic - this is already done via indexServiceFactory in ml/public/application/util/index_service.ts - this file will eventually replace this utils file.

Now I'm thinking it would have been clearer to add a comment to this soon-to-be-deprecated file saying it will be replaced by the one I mentioned above and that that one should be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Luckily all remaining usages of this function were in places where we could make the new service easily available so I was able to get rid of this stand-alone function in 70ab770. 🥳

const httpService = new HttpService(httpStart);
const mlApiServices = mlApiServicesProvider(httpService);
const mlIndexUtils = indexServiceFactory(dataViews);
const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils);
Copy link
Contributor

Choose a reason for hiding this comment

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

To prevent bloat of the context, perhaps the mlFieldFormatService should be created in the component as needed and then passed/made available to it's children?

For example, we already have mlApiServices and dataViews in context, I believe.
fieldFormatServiceFactory requires mlApiServices and mlIndexUtils - which, in turn, only requires the dataViewsService.

From that, fieldFormatService could be instantiated once in the component in which it is needed instead of adding more to the context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure it's bloat? Prior to this PR the service was only instantiated once too, it's just that with the dependency cache under the hood it could be imported everywhere without being passed around via context. So this variant felt closer to the original then instantiating multiple times. Note the factory doesn't have any costly side effects, for example it doesn't call any requests, that's only happening once you call one of its methods.

Copy link
Member

Choose a reason for hiding this comment

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

I tend to agree that it would be better to keep mlIndexUtils and mlFieldFormatService out of this context as they are used so rarely throughout our plugin. It sets a precedence that all utils and services we have should go in here too.
Instantiating them when needed in each file would be better IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As discussed offline, I'm leaving mlFieldFormatService here as a global service for now because of its internal state. mlIndexUtils will not be passed on to global services, instead it can be accessed within components via useMlIndexUtils().

I added the following comment to each place where the services are set up (the main ML app, the anomaly charts embeddable and the single metric viewer embeddable):

    // Note on the following services:
    // - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`,
    //   but it's not being made available as part of global services. Since it's just
    //   some stateless utils `useMlIndexUtils()` should be used from within components.
    // - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache",
    //   so because of its own state it needs to be made available as a global service.
    //   In the long run we should again try to get rid of it here and make it available via
    //   its own context or possibly without having a singleton like state at all, since the
    //   way this manages its own state right now doesn't consider React component lifecycles.

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 still require a code change? mlIndexUtils is still being added to the context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yes, overlooked that one, thanks! Fixed in bccd244.

@@ -104,7 +104,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => {

const getAnomaliesMapsLink = async (anomaly: MlAnomaliesTableRecord) => {
const index = job.datafeed_config.indices[0];
const dataViewId = await getDataViewIdFromName(index);
const dataViewId = await getDataViewIdFromName(data.dataViews, index);
Copy link
Contributor

Choose a reason for hiding this comment

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

As mentioned after James's comment below - the indexServiceFactory in ml/public/application/util/index_service.ts (which is meant to replace the utils file getDataViewIdFromName is pulled from here) should be used here to get that functionality. It only requires dataViewsService, which should be available in this component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 70ab770.

@@ -97,7 +97,7 @@ export const useResultsViewConfig = (jobId: string) => {

try {
const destIndex = getDestinationIndex(jobConfigUpdate);
const destDataViewId = (await getDataViewIdFromName(destIndex)) ?? destIndex;
const destDataViewId = (await getDataViewIdFromName(dataViews, destIndex)) ?? destIndex;
Copy link
Contributor

@alvarezmelissa87 alvarezmelissa87 Feb 13, 2024

Choose a reason for hiding this comment

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

Same note here for either instantiating indexServiceFactory with the necessary services available via useMlKibana or passing in the service or function after instantiation wherever this hook is used.

This is the long term solution @jgowdyelastic and I chatted about for moving away from the dependency cache and also preventing making the context too bloated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 70ab770.

@@ -138,7 +139,7 @@ export const Controls: FC<Props> = React.memo(
const nodeType = selectedNode?.data('type');

const onCreateJobClick = useCallback(async () => {
const dataViewId = await getDataViewIdFromName(nodeLabel);
const dataViewId = await getDataViewIdFromName(data.dataViews, nodeLabel);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in 70ab770.

useEffect(() => {
const explorerService = explorerServiceFactory(mlFieldFormatService);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same note here - mlFieldFormatService doesn't need to be pulled from context but can be instantiated in this component once (using the required services already in context) to be passed on.

@@ -83,7 +85,10 @@ export class ExplorerChartDistribution extends React.Component {
return;
}

const fieldFormat = mlFieldFormatService.getFieldFormat(config.jobId, config.detectorIndex);
const fieldFormat = this.context.services.mlServices.mlFieldFormatService.getFieldFormat(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here for instantiating the fieldformatService on the fly.

@alvarezmelissa87
Copy link
Contributor

Gave this another test and getting blank charts now along with this toast:
image

Looks like the job service has a dependency cache dependency that could be causing issues. I think it tries to use the http service from the dependency cache.
Getting these errors in console:

image image

For this ^ looks like the path might be wrong - I think it should be this.context.services.mlServices.mlApiServices.results

@walterra
Copy link
Contributor Author

@alvarezmelissa87 Thanks for the testing and spotting the errors!

  • The first toast is not related to this PR, you need to update your ES instance because the Kibana data views service makes use of new functionality.
  • I was able to fix mlJobService to make it work in embeddables in 3ee830d.
  • Well spotted on the wrong context access for mlApiServices.results, that one's fixed too.

@walterra walterra requested a review from a team as a code owner February 15, 2024 10:25
@botelastic botelastic bot added the Team:obs-ux-infra_services Observability Infrastructure & Services User Experience Team label Feb 15, 2024
@elasticmachine
Copy link
Contributor

Pinging @elastic/obs-ux-infra_services-team (Team:obs-ux-infra_services)

@walterra walterra added v8.14.0 and removed v8.13.0 labels Feb 15, 2024
@walterra walterra removed the request for review from a team February 16, 2024 11:14
@kibana-ci
Copy link
Collaborator

💛 Build succeeded, but was flaky

Failed CI Steps

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
ml 3.7MB 3.7MB +12.9KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
ml 79.0KB 78.4KB -691.0B
Unknown metric groups

async chunk count

id before after diff
ml 44 45 +1

ESLint disabled line counts

id before after diff
ml 560 563 +3

Total ESLint disabled count

id before after diff
ml 563 566 +3

History

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

cc @walterra

Copy link
Contributor

@peteharverson peteharverson left a comment

Choose a reason for hiding this comment

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

Tested and LGTM

Copy link
Member

@jgowdyelastic jgowdyelastic left a comment

Choose a reason for hiding this comment

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

LGTM

@walterra walterra merged commit dda8c8f into elastic:main Mar 5, 2024
18 checks passed
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Mar 5, 2024
@walterra walterra deleted the 176651-ml-smv-embeddable-click branch March 5, 2024 12:54
@walterra walterra removed the Team:obs-ux-infra_services Observability Infrastructure & Services User Experience Team label Mar 5, 2024
jgowdyelastic added a commit that referenced this pull request Apr 15, 2024
Fixing bug introduced in #176788
The `jobService` hasn't been initialised when being used to load the
groups.
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 bug Fixes for quality problems that affect the customer experience Feature:Anomaly Detection ML anomaly detection :ml release_note:skip Skip the PR/issue when compiling release notes v8.14.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants