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

[Dashboard][Embeddable] Create Explicit Diffing System #121241

Merged
merged 18 commits into from
Jan 11, 2022

Conversation

ThomThomson
Copy link
Contributor

Summary

Fixes #98180
Fixes #117960

This PR creates a new system within the embeddable plugin to provide robust input-diffing capabilities. Instead of the dashboard app diffing all of its state the same way, using Lodash's isEqual or equivalent, each child embeddable is in charge of determining whether or not its own current explicit input is equal to another input object given by the dashboard (the last saved state). To implement this, two functions have been added to the generic embeddable:

  • getExplicitInput - is an explicit function / clarification to the embeddable system which allows embeddables to more easily differentiate between what is their own explicit input, and what is input which was passed to them by a parent. If the embeddable does not have a parent, the results from getExplicitInput and getInput should be the same.
  • getExplicitInputIsEqual - is a diffing function which takes one input object and diffs it against its current explicitInput. The generic embeddable has a generic and fairly comprehensive implementation of this function which should work in most cases. Any embeddable type which has input keys that should be ignored when diffing can override this function.

Additionally, as an example of why these new API features are useful, the Maps Embeddable was given an implementation of getExplicitInputIsEqual which ignores mapBuffer, thus solving #98180.

Checklist

Delete any items that are not applicable to this PR.

@ThomThomson ThomThomson added Feature:Dashboard Dashboard related features Feature:Embedding Embedding content via iFrame release_note:fix Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas loe:large Large Level of Effort v8.0.0 impact:medium Addressing this issue will have a medium level of impact on the quality/strength of our product. v8.1.0 labels Dec 14, 2021
@ThomThomson ThomThomson marked this pull request as ready for review December 15, 2021 15:37
@ThomThomson ThomThomson requested review from a team as code owners December 15, 2021 15:37
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-presentation (Team:Presentation)

Copy link
Contributor

@Dosant Dosant left a comment

Choose a reason for hiding this comment

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

I am in progress of processing the change :D

@drewdaemon
Copy link
Contributor

drewdaemon commented Dec 16, 2021

It looks like this will work nicely with our in-progress custom Lens equality function. 👍

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Contributor

@nreese nreese left a comment

Choose a reason for hiding this comment

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

maps changes LGTM
code review, tested in chrome.

@Dosant
Copy link
Contributor

Dosant commented Dec 21, 2021

@elasticmachine merge upstream

@@ -262,9 +262,13 @@ export const useDashboardAppState = ({
dashboardBuildContext.$checkForUnsavedChanges,
])
.pipe(debounceTime(DashboardConstants.CHANGE_CHECK_DEBOUNCE))
.subscribe((states) => {
.subscribe(async (states) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Because of async piece, I think we should put this logic into switchMap to avoid race conditions

Copy link
Contributor Author

@ThomThomson ThomThomson Dec 22, 2021

Choose a reason for hiding this comment

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

Good call @Dosant !
I didn't think of it, because the function in its current form is almost synchronous due to the only async piece being the untilEmbeddableLoaded method which is synchronous unless the dashboard is loading.

That said, this could have easily caused race conditions if a downstream implementation used a long-running task in their diffing method. I added a switchmap operator to the pipe and return when the inner observable is closed. I tested by adding a one second delay to the diff_dashboard_state function, firing off many changes in quick succession, and ensuring that only the latest changes are diffed.

Copy link
Contributor

@Dosant Dosant Dec 29, 2021

Choose a reason for hiding this comment

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

nit: maybe it can be simpler with wrapping promise into an observable using from:

stream$.pipe(switchMap((v) => from(op(v)))).subscribe((v) => {
 // logic
});

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Contributor

@vadimkibana vadimkibana left a comment

Choose a reason for hiding this comment

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

Can the embeddable code be refactored such that we don't need a deep comparison? Instead we would only do a quick equality check? Say

const isInputEqual = embeddable.getInput() === newInput

If it is not possible as-is, because on each embeddable state change we need to merge persisted state with the embeddable local UI state, maybe we could separate those two concepts? And then use a quick equality check for those?

const isPersistentInputEqual = embeddable.getPersistentInput() === newPersistentInput
const isStateEqual = embeddable.getState() === newState

Copy link
Contributor

@vadimkibana vadimkibana left a comment

Choose a reason for hiding this comment

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

AppServices code changes LGTM. We discussed the comments in this PR offline and some of the ways of improving embeddable state management.


Not related to this PR, some ideas for future:

  • Remove deep-clone usage in embeddable input.
  • Separate embeddable (1) persistable state from (2) UI state.
  • Start embeddable working group, to discuss embeddable plugin future improvements.

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

Copy link
Contributor

@poffdeluxe poffdeluxe left a comment

Choose a reason for hiding this comment

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

LGTM

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

@ThomThomson
Copy link
Contributor Author

@elasticmachine merge upstream

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
embeddable 73 74 +1
maps 812 813 +1
total +2

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
embeddable 374 384 +10
maps 206 208 +2
total +12

Async chunks

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

id before after diff
dashboard 272.7KB 273.1KB +421.0B
lens 1022.7KB 1022.6KB -118.0B
maps 2.6MB 2.6MB +965.0B
total +1.2KB

Page load bundle

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

id before after diff
embeddable 63.3KB 64.4KB +1.1KB
Unknown metric groups

API count

id before after diff
embeddable 452 465 +13
maps 207 209 +2
total +15

References to deprecated APIs

id before after diff
dashboard 254 241 -13

History

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

Copy link
Contributor

@stratoula stratoula left a comment

Choose a reason for hiding this comment

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

Vis editors changes LGTM. I tested the embeddable functionality in Chrome and it seems to work fine :)

@ThomThomson
Copy link
Contributor Author

💚 All backports created successfully

Status Branch Result
8.0

Note: Successful backport PRs will be merged automatically after passing CI.

ThomThomson added a commit to ThomThomson/kibana that referenced this pull request Jan 11, 2022
Co-authored-by: Anton Dosov <[email protected]>
Co-authored-by: nreese <[email protected]>
(cherry picked from commit 944ccf1)
ThomThomson added a commit that referenced this pull request Jan 11, 2022
…2668)

Co-authored-by: Anton Dosov <[email protected]>
Co-authored-by: nreese <[email protected]>
(cherry picked from commit 944ccf1)
ThomThomson added a commit to ThomThomson/kibana that referenced this pull request Feb 1, 2022
Co-authored-by: Anton Dosov <[email protected]>
Co-authored-by: nreese <[email protected]>
(cherry picked from commit 944ccf1)

# Conflicts:
#	src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts
#	x-pack/plugins/maps/server/plugin.ts
ThomThomson added a commit that referenced this pull request Feb 2, 2022
… (#124293)

* [Dashboard][Embeddable] Create Explicit Diffing System (#121241)

Co-authored-by: Anton Dosov <[email protected]>
Co-authored-by: nreese <[email protected]>
(cherry picked from commit 944ccf1)

# Conflicts:
#	src/plugins/dashboard/public/application/lib/diff_dashboard_state.ts
#	x-pack/plugins/maps/server/plugin.ts

* type fix

* replace ecommerce sample map id
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Dashboard Dashboard related features Feature:Embedding Embedding content via iFrame impact:medium Addressing this issue will have a medium level of impact on the quality/strength of our product. loe:large Large Level of Effort release_note:fix Team:Presentation Presentation Team for Dashboard, Input Controls, and Canvas v8.0.0 v8.1.0
Projects
None yet
10 participants