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

[data views] cache field caps requests #168910

Merged

Conversation

mattkime
Copy link
Contributor

@mattkime mattkime commented Oct 15, 2023

Summary

Closes: #169622

Field caps request caching - implement stale-while-revalidate cache headers and etags with 304 responses as appropriate.

This PR accomplishes

  • Adds 304 Not Modified to http server
  • Adds refresh button to data view management that force refreshes field list
  • Adds /internal/data_views/fields endpoint which supports caching.
    • This is necessary since fields_for_wildcard doesn't support caching due to POST requests
    • Adds stale-while-revalidate header directive UNLESS field list is empty.
  • Uses Vary header with hash of user id to force requests when user has changed.
  • Unchanged field list responses won't recreate data view field list

How to test

  1. Pop open the dev tools to the network tab and make sure 'Disable cache' is unchecked. filter for 'fields' requests
  2. Load more than one data set (sample data is fine)
  3. Go to discover
  4. Switch selected data views. Notice status code on first load vs subsequent loads
  5. Open a new window, notice loading from cache
  6. Push document that changes field list. You'll need to wait for cache to expire for it to load. The default is 5s.
  7. Notice that field list loads after cache is old properly result in 304 response.
  8. Set data_views:cache_max_age, shift reload. No field requests will be cached.

Note on Safari

Safari doesn't support the stale-while-revalidate directive. Additionally, the Safari dev console shows 304 responses as 200's. I figured this out by adding console statements to the fields endpoint. As best I can tell, Safari won't be performant on slow clusters but its also no slower than it was before this PR. Safari does respect the disabling of cache headers.

Release note

Data View field list requests are now cached with a stale-while-revalidate strategy. This means that after the initial field list request, all subsequent requests return the cached response which is very fast. If the cache is determined to be stale then the cache will update in the background and new data will be available on the next request.

This behavior can be modified via the data_views:cache_max_age Kibana advanced setting. Setting it to zero will disable the cache. All other values (in seconds) will be used to determine whether the cache is stale. The default value is 5 seconds.

The field list can be manually updated via the refresh button in data view management or a hard refresh with your browser.

Note for Safari: The stale-while-revalidate cache directive is unsupported, therefore it makes additional requests. If this is impacting Kibana performance then try Chrome or Firefox.

Data View Management

Screenshot 2024-01-09 at 1 36 20 PM

@mattkime mattkime changed the title add stale while revalidate [data views] cache field caps requests Oct 20, 2023
@kertal kertal marked this pull request as ready for review October 20, 2023 13:16
@kertal kertal requested a review from a team as a code owner October 20, 2023 13:16
Copy link
Contributor

@davismcphee davismcphee 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 looking into this, and it definitely feels like a step in the right direction. In my local testing, it seems to work reliably in some situations and less so in others. For example, Data View Management seems to fairly reliably cache the requests across page navigations and page reloads, but the same requests in Discover weren't being cached reliably. Even without a page reload, it seemed like the cache wasn't being used reliably for duplicate GET _fields_for_wildcard requests.

HTTP caching is pretty hard to understand and troubleshoot, but after looking into it I suspect this is at least partly related to cache validation requests on page reload. It's possible we can improve this using ETag and/or Last-Modified headers, but I'm not entirely sure.

Some possibly related SO questions I encountered while looking into this (adding here for future reference):

All of this makes me wonder if maybe we should be taking more control over the caching than what HTTP headers provide us. Even if we figure out the reload issue, the Cache-Control headers are still only a hint to the browser about what it's allowed to do, which it may choose to ignore anyway. I think exploring HTTP headers was a good idea and may still be the right approach in the end (and I think the stale-while-revalidate strategy is the way to go regardless of how we implement it), but for completeness I think it's also worth us exploring a custom stale-while-revalidate strategy that interacts directly with the browser cache through the cache API:

@kertal kertal added the Team:DataDiscovery Discover, search (e.g. data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. label Oct 31, 2023
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-data-discovery (Team:DataDiscovery)

@kertal
Copy link
Member

kertal commented Oct 31, 2023

Just did some basic testing with a dashboard containing 4 ES|QL based embeddables using the same index pattern, with Chrome using simulated slow network conditions, the fields fetching in our current iteration would take about 6s in this case

Bildschirmfoto 2023-10-31 um 20 49 04

Initial loading, there is just 1 uncached request instead of 5, taking about 2s, the 4 follow up requests are cached and just take 1-2ms
image
So it already is much faster (about 2s instead of 6s)

When loading the dashboard again with a browser reload, all requests are cached, so instead of 10s it just takes a few milliseconds

image

@mattkime mattkime requested a review from a team as a code owner November 2, 2023 03:08
@mattkime mattkime added Feature:Data Views Data Views code and UI - index patterns before 8.0 release_note:enhancement labels Nov 2, 2023
Copy link
Contributor

@pgayvallet pgayvallet left a comment

Choose a reason for hiding this comment

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

LGTM for Core changes, just one remark

@mattkime mattkime requested review from a team as code owners November 5, 2023 02:41
@mattkime mattkime requested review from pzl and dasansol92 November 5, 2023 02:41
Copy link
Member

@lukasolson lukasolson left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Contributor

@davismcphee davismcphee left a comment

Choose a reason for hiding this comment

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

Tested locally one last time, and the remaining two issues I reported have been resolved -- thanks for updating the tests too 👍 Great work on this, and I think it's time to get this merged in. Thanks for all the work on it, and LGTM!


expect(fetchSpy).toHaveBeenCalledWith(expectedPath, expect.any(Object));
expect(fetchSpy).toHaveBeenCalledWith(expectedPath, {
// not sure what asResponse is but the rest of the results are useful
Copy link
Contributor

Choose a reason for hiding this comment

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

I think asResponse just prevents the HTTP service from automatically parsing the response as JSON so we can deal with it in its raw form (which makes sense since we're doing ETags, etc.).

@@ -104,7 +104,8 @@ export class DataViewsApiClient implements IDataViewsApiClient {
allow_no_index: allowNoIndex,
include_unmapped: includeUnmapped,
fields,
allow_hidden: allowHidden,
// default to undefined to keep value out of URL params and improve caching
allow_hidden: allowHidden || undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

This approach is much nicer than addressing each caller separately 👍 no need to try to remember it in future changes.

@kertal kertal requested a review from tonyghiani January 15, 2024 18:56
@mattkime mattkime requested a review from tonyghiani January 15, 2024 19:50
@kibana-ci
Copy link
Collaborator

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] Jest Tests #6 / useActionTypes should fetch action types

Metrics [docs]

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
@kbn/core-http-router-server-internal 26 27 +1
@kbn/core-http-server 179 180 +1
data 2569 2579 +10
dataViews 257 273 +16
total +28

Any counts in public APIs

Total count of every any typed public API. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats any for more detailed information.

id before after diff
@kbn/core-http-router-server-internal 6 7 +1

Async chunks

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

id before after diff
dataViewManagement 135.4KB 136.3KB +916.0B
discover 564.1KB 564.1KB +3.0B
eventAnnotationListing 196.9KB 196.9KB +3.0B
lens 1.4MB 1.4MB +3.0B
total +925.0B

Page load bundle

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

id before after diff
dataViews 46.9KB 48.3KB +1.4KB
Unknown metric groups

API count

id before after diff
@kbn/core-http-router-server-internal 26 27 +1
@kbn/core-http-server 454 457 +3
data 3220 3230 +10
dataViews 922 938 +16
total +30

ESLint disabled line counts

id before after diff
dataViewManagement 2 3 +1

Total ESLint disabled count

id before after diff
dataViewManagement 2 3 +1

History

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

cc @mattkime

@mattkime mattkime requested review from tonyghiani and removed request for tonyghiani January 15, 2024 20:29
Copy link
Contributor

@tonyghiani tonyghiani left a comment

Choose a reason for hiding this comment

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

logs-shared changes LGTM!

@mattkime mattkime merged commit acdaf01 into elastic:main Jan 16, 2024
40 checks passed
@kibanamachine kibanamachine added v8.13.0 backport:skip This commit does not require backporting labels Jan 16, 2024
mattkime added a commit that referenced this pull request Jan 22, 2024
## Summary

tldr; The implements a fallback sha1 method when using the browser in an
insecure context.

The data views fields requests cache need uniqueness across users. This
has been implemented by hashing the user object into a HTTP header using
sha1. Typically we can use the browser's built in crypto objects for
this HOWEVER its not available in insecure contexts -
https://www.chromium.org/blink/webcrypto/#accessing-it - this PR
supplies a sha1 function for insecure contexts.

How to test - when running kibana locally, it will run in a secure
context via 127.0.0.1 or localhost. It will run in an insecure context
at 0.0.0.0. Simply load some sample data and load a data view.

follow up to #168910

Screenshot of error resolved by this pr - 
![Visualize Visualize Reporting Screenshots Print PDF button becomes
available
whe-c57ca69f29465527c4079569a4548eb10fe0568302776500148260b299fbd5c4](https://github.com/elastic/kibana/assets/216176/d7bcec41-631f-426f-b209-87b2d6403f23)

---------

Co-authored-by: kibanamachine <[email protected]>
CoenWarmer pushed a commit to CoenWarmer/kibana that referenced this pull request Feb 15, 2024
…#175181)

## Summary

tldr; The implements a fallback sha1 method when using the browser in an
insecure context.

The data views fields requests cache need uniqueness across users. This
has been implemented by hashing the user object into a HTTP header using
sha1. Typically we can use the browser's built in crypto objects for
this HOWEVER its not available in insecure contexts -
https://www.chromium.org/blink/webcrypto/#accessing-it - this PR
supplies a sha1 function for insecure contexts.

How to test - when running kibana locally, it will run in a secure
context via 127.0.0.1 or localhost. It will run in an insecure context
at 0.0.0.0. Simply load some sample data and load a data view.

follow up to elastic#168910

Screenshot of error resolved by this pr - 
![Visualize Visualize Reporting Screenshots Print PDF button becomes
available
whe-c57ca69f29465527c4079569a4548eb10fe0568302776500148260b299fbd5c4](https://github.com/elastic/kibana/assets/216176/d7bcec41-631f-426f-b209-87b2d6403f23)

---------

Co-authored-by: kibanamachine <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
apm:review backport:skip This commit does not require backporting ci:collect-apm Feature:Data Views Data Views code and UI - index patterns before 8.0 release_note:enhancement Team:DataDiscovery Discover, search (e.g. data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. v8.13.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[DataViews] Add caching for the request of fields