-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
Optimize saved objects getScopedClient and HTTP API #68221
Conversation
I took a look at this locally and it looks outstanding. I'm happy to provide more details about recreating the test or viewing the cpu profiles but here are some notes Test1000 connections to TL;DR: Huge improvements in key areas RPS: 34 -> 178 MasterCompleted 2K requests. 4K errors PRCompletes 11K requests. 4K errors [1] Chose |
I initially showed tests against This is hitting Sorry for the "after"/"before" arrangement but the event loop improvements are still present enroll command
|
I've pushed some more changes and I think we've now removed all the low hanging fruit. Most of the time is now spent in validating the request which takes 0.4ms or ~10% of the time
This will probably be a lot more with more complex validation, but we will have to profile the fleet API again to see what specifically is taking up time in that route. |
I did some tests also and CPU profile while enrolling 250 agents, and the improvments seems to be great, next bottleneck are on our side :) cpu-profile-after.cpuprofile.zip These one still seems to be improved, I think it's related to used |
Yeah it looks like we're hitting performance bottleneck in the KQL parser, fun times |
create() { | ||
return coreContext.configService.atPath(pluginManifest.configPath); | ||
create<T>() { | ||
return coreContext.configService.atPath<T>(pluginManifest.configPath).pipe(shareReplay(1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spaces plugin takes a config value for every getScopedClient
call (every incoming request). This does config validation for this config path each time.
kibana/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts
Lines 75 to 82 in c5e446a
const getScopedClient = async (request: KibanaRequest) => { | |
const [coreStart] = await getStartServices(); | |
const internalRepository = await internalRepositoryPromise; | |
return config$ | |
.pipe( | |
take(1), | |
map((config) => { |
We could optimize the plugin to not take a new config value every time but only if a new value was emitted, but this felt like a better fix that will probably benefit other places.
@@ -136,7 +136,7 @@ export class SavedObjectsRepository { | |||
injectedConstructor: any = SavedObjectsRepository | |||
): ISavedObjectsRepository { | |||
const mappings = migrator.getActiveMappings(); | |||
const allTypes = Object.keys(getRootPropertiesObjects(mappings)); | |||
const allTypes = typeRegistry.getAllTypes().map((t) => t.name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apparently Object.entries()
which gets called from inside getRootPropertiesObjects
is very slow.
Pierre already refactored many places where we use this round about getRootPropertiesObjects
way to get all the types, but there's still a few remaining.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some getRootPropertiesObjects
were kept because of the config
'virtual' type that wasn't properly registered to the registry. But this has been resolved since, so we're safe to remove all the remaining usages.
Very surprised Object.keys
is a bottleneck, even if it makes sense that typeRegistry.getAllTypes()
is faster as the registry is based on a Map
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably it's not Object.keys
but getRootPropertiesObjects
mutating an object?
acc[key] = value; |
v8 cannot optimizie this case https://richardartoul.github.io/jekyll/update/2015/04/26/hidden-classes.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I misread and thought it was just a call to getRootProperties
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the line that showed up in the profiler and took almost 5ms:
return Object.entries(rootProperties).reduce((acc, [key, value]) => { |
const getScopedClient = async (request: KibanaRequest) => { | ||
const [coreStart] = await getStartServices(); | ||
const internalRepository = await internalRepositoryPromise; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
createScopedRepository
already creates a new repository for every request, so after the other improvements it's probably not that critical that we re-use this repository instead of creating a new one. But since this is on the hot path it felt like it doesn't hurt to keep this minor improvement around.
Pinging @elastic/kibana-platform (Team:Platform) |
💚 Build SucceededHistory
To update your PR or re-run it, just comment with: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -136,7 +136,7 @@ export class SavedObjectsRepository { | |||
injectedConstructor: any = SavedObjectsRepository | |||
): ISavedObjectsRepository { | |||
const mappings = migrator.getActiveMappings(); | |||
const allTypes = Object.keys(getRootPropertiesObjects(mappings)); | |||
const allTypes = typeRegistry.getAllTypes().map((t) => t.name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some getRootPropertiesObjects
were kept because of the config
'virtual' type that wasn't properly registered to the registry. But this has been resolved since, so we're safe to remove all the remaining usages.
Very surprised Object.keys
is a bottleneck, even if it makes sense that typeRegistry.getAllTypes()
is faster as the registry is based on a Map
.
We are removing this code in v7.10 anyway |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
@rudolf Really excited to see this performance improvement. I wonder if there is a way that we keep "track" of these improvements to make sure not a follow up change in KB accidentally invalidates this optimisation again. |
* master: (57 commits) Add app arch team as owner of datemath package (elastic#66880) [Observability] Landing page for Observability (elastic#67467) [SIEM] Fix timeline buildGlobalQuery (elastic#68320) Optimize saved objects getScopedClient and HTTP API (elastic#68221) [Maps] Fix mb-style interpolate style rule (elastic#68413) update script to always download node (elastic#68421) [SECURITY SOLEIL] Fix selection of event type when no siem index signal created (elastic#68291) [DOCS] Adds note about configuring File Data Visualizer (elastic#68407) [DOCS] Adds link from remote clusters to index patterns (elastic#68406) [QA] slack notify on failure (elastic#68126) upgrade eslint-plugin-react-hooks from 2.3.0 to 4.0.4 (elastic#68295) moving to jira to a gold license (elastic#67178) [DOCS] Revises doc on adding data (elastic#68038) [APM] Add ThemeProvider to support dark mode (elastic#68242) Make welcome screen disabling first action in loginIfPrompted (elastic#68238) [QA] Code coverage: unskip tests, collect tests results, exclude bundles from report (elastic#64477) [ML] Functional tests - disable flaky regression and classification creation test [Alerting] change eventLog ILM requests to absolute URLs (elastic#68331) Report page load asset size (elastic#66224) [SIEM][CASE] Change SIEM to Security (elastic#68365) ...
* Create a single repository to be shared by all calls to getScopedClient * Cache migrator.getActiveMappings to improve createRepository * Use typeregistry.getAllTypes instead of getRootPropertiesObjects(mappings) * Don't validate plugin's config every time it's read * Fix saved_objects_mixin Co-authored-by: Elastic Machine <[email protected]>
Summary
When the spaces plugin is enabled to secure the saved objects client and the HTTP API we create several saved object repositories for every request. Because we're re-calculating the active mappings for every saved objects repository that's created this creates a performance bottleneck.
This PR optimizes the performance by caching the active mappings making it faster to create repositories and also by re-using the same repository in the spaces client's getScopedClient method.
It further uses
shareReplay
for the the config$ observable we provide to plugins to prevent running config path validation every time a plugin reads from it's config observable.Note: There's also a lot of time spent in legacy code that augments the hapi request object with uisettings which is unnecessary, but I'm leaving that as is for now
kibana/src/legacy/ui/ui_settings/ui_settings_service_for_request.ts
Line 40 in 5676ac0
Thanks for the profiling work @nchaulet and the fleet team.
Checklist
Delete any items that are not applicable to this PR.
For maintainers