-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Batch mget requests on dashboard load and cache field_stat results #10081
Conversation
Every now and then, I'll have one of the field stats request take a long time (>60 seconds). Only sending one field stats request per dashboard has been very beneficial in speed for me. |
@trevan You tested this PR and found it helped? Or do you mean you have already implemented logic to send only one field_stats api request and found it helped? |
@stacey-gammon, I already had extremely similar logic to what you have and found it very helpful. |
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.
Sorry I let this sit for so long, I've been heads down on #9853 for a while
@@ -127,7 +127,7 @@ export default function SavedObjectFactory(esAdmin, kbnIndex, Promise, Private, | |||
* @return {Promise} | |||
* @resolved {SavedObject} | |||
*/ | |||
this.init = _.once(() => { | |||
this.init = _.once((isDefered) => { |
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.
A few observations/questions:
_.once()
'd functions should almost never accept arguments- What does
isDefered
mean? What is/should be defered? The Saved Object? - Calling the
docSource._createRequest
method from here feels like a violation of the intended public api for the docSource. Why not just change howdocSource.fetch()
functions?
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.
@spalger isDefered
tells init how to load the saved object. When false, the saved object is fetched immediately. When true, the saved object fetch request is placed on the request queue ... it is deferred.
The PR was just trying to keeping things simple. I did not want to re-engineer anything. Kibana already had the concept of a request queue and I figured this was a good use for it.
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.
Yeah, I think that's a good idea. If we want to keep the current code I would probably change the variable to deferDocSourceRequest
(my second comment was more about naming).
That said, I think all calls to docSource.fetch()
should probably be using the request queue. I don't think there is any reason to require requesters to opt-into this style of fetching. One wrinkle in that is the interval at which the request queue is cleared. We might need to have some debounced request clearing trigger that gets triggered each time docSource.fetch()
is called on any source...
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.
Maybe something like this (pseudo code):
// DocSource class
fetch() {
const request = new Request({ urgent: true });
requestQueue.addNew(request)
return request.defer.promise
}
// in RequestQueue class
addNew(req) {
this._all.push(req);
this._scheduleCheck()
}
_scheduleCheck() {
if (this._pendingCheck) {
clearTimeout(this._pendingCheck)
}
this._pendingCheck = setTimeout(
() => this._checkForReadyRequest(),
CLEAR_REQUEST_QUEUE_DEBOUNCE_MS
)
}
_checkForReadyRequests() {
return this._all.filter(req => {
return req.isUrgent() || req.isNaturallyReady()
})
}
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.
That said, I think all calls to docSource.fetch() should probably be using the request queue.
What about when just retrieving a single object by id? E.g. this gets called when you save a dashboard because it reloads the dashboard you just saved by id. In that case, maybe it's not worth the overhead? It looks like the looper checks for requests every 1.5 seconds, so that's adding an average .75s delay when just getting a single object.
That said, I agree about the naming, but what about batchRequest? I like batch
better than defer
, as it gives more reasoning behind it.
I feel like the entire batching logic could be improved to circumvent the looper delay entirely. Send in an array of savedObjects, or ids, and grab them all at once in a single batch (or split batches up to avoid any one batch from being too large if that was a potential problem). But that would be too large of a code overhaul at this point, so IMO, just doing the rename might be the best way to go at this point.
@@ -15,6 +15,8 @@ export default function FetchStrategyForSearch(Private, Promise, timefilter, kbn | |||
* @return {Promise} - a promise that is fulfilled by the request body | |||
*/ | |||
reqsFetchParamsToBody: function (reqsFetchParams) { | |||
const indexToListMapping = {}; |
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.
👍
Just pushed updates, not really qualified to review anymore
@nreese, @stacey-gammon and I worked on integrating the batching into the courier itself, I just pushed the changes we worked on that make all calls to @pickypg another review from you would be helpful too |
src/ui/public/courier/fetch/fetch.js
Outdated
else return fetchThese(requests); | ||
} | ||
const debouncedFetchThese = _.debounce(() => { | ||
const requests = requestQueue.get().filter(req => req.isFetchRequested()); |
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.
Did this end up not being a problem with requests being fetched more than once?
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.
Oops, forgot to update this to the new req.isFetchRequestedAndPending()
method that takes requests' started state into account.
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.
One minor comment, otherwise looks good! Though some of this is my code, so should have another reviewer too,
* @return {Boolean} | ||
*/ | ||
isFetchRequestedAndPending() { | ||
return !!this._fetchRequested && !this.started; |
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.
Why the need for the double negative? Is it to avoid the undefined case? It'll still all work as expected right? Maybe just initialize this._fetchRequested to false in that case?
Just seems a little strange when the value is essentially a boolean, and the double negative requires an extra extra brain cycle or two to make the conversion. :)
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.
Forces boolean true
out of anything. I agree that it's unusual since it's part of a larger boolean statement and now I wonder if there's more to the trick than I remember or just a force of habit.
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.
Yeah, when it was just returning the value of _fetchRequested
I was a little more concerned about it always being a boolean value, but now that it's returning the &&
calculation it's totally unnecessary.
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
Backports PR #10081 **Commit 1:** defer loading visualization saved objects so they can be loaded in a single _mget * Original sha: 5c1344e * Authored by nreese <[email protected]> on 2017-01-24T23:41:05Z **Commit 2:** Merge branch 'defer' of https://github.com/nreese/kibana into nreese-defer * Original sha: 96af3ea * Authored by Stacey Gammon <[email protected]> on 2017-01-26T13:44:55Z **Commit 3:** Don't request field stats more than once for the same index pattern * Original sha: 9a02e5b * Authored by Stacey Gammon <[email protected]> on 2017-01-26T15:57:34Z **Commit 4:** [ui/courier] batch fetch requests for all searches and docs * Original sha: 20d55fe * Authored by spalger <[email protected]> on 2017-02-06T22:13:23Z **Commit 5:** [ui/courier] remove remaining mentions of req.isFetchRequested() * Original sha: f5bd5ca * Authored by spalger <[email protected]> on 2017-02-08T21:22:32Z **Commit 6:** [courier/fetch/request] remove unneceessary !! * Original sha: eb5446d * Authored by spalger <[email protected]> on 2017-02-09T20:48:48Z
Backports PR #10081 **Commit 1:** defer loading visualization saved objects so they can be loaded in a single _mget * Original sha: 5c1344e * Authored by nreese <[email protected]> on 2017-01-24T23:41:05Z **Commit 2:** Merge branch 'defer' of https://github.com/nreese/kibana into nreese-defer * Original sha: 96af3ea * Authored by Stacey Gammon <[email protected]> on 2017-01-26T13:44:55Z **Commit 3:** Don't request field stats more than once for the same index pattern * Original sha: 9a02e5b * Authored by Stacey Gammon <[email protected]> on 2017-01-26T15:57:34Z **Commit 4:** [ui/courier] batch fetch requests for all searches and docs * Original sha: 20d55fe * Authored by spalger <[email protected]> on 2017-02-06T22:13:23Z **Commit 5:** [ui/courier] remove remaining mentions of req.isFetchRequested() * Original sha: f5bd5ca * Authored by spalger <[email protected]> on 2017-02-08T21:22:32Z **Commit 6:** [courier/fetch/request] remove unneceessary !! * Original sha: eb5446d * Authored by spalger <[email protected]> on 2017-02-09T20:48:48Z
…10273) Backports PR #10081 **Commit 1:** defer loading visualization saved objects so they can be loaded in a single _mget * Original sha: 5c1344e * Authored by nreese <[email protected]> on 2017-01-24T23:41:05Z **Commit 2:** Merge branch 'defer' of https://github.com/nreese/kibana into nreese-defer * Original sha: 96af3ea * Authored by Stacey Gammon <[email protected]> on 2017-01-26T13:44:55Z **Commit 3:** Don't request field stats more than once for the same index pattern * Original sha: 9a02e5b * Authored by Stacey Gammon <[email protected]> on 2017-01-26T15:57:34Z **Commit 4:** [ui/courier] batch fetch requests for all searches and docs * Original sha: 20d55fe * Authored by spalger <[email protected]> on 2017-02-06T22:13:23Z **Commit 5:** [ui/courier] remove remaining mentions of req.isFetchRequested() * Original sha: f5bd5ca * Authored by spalger <[email protected]> on 2017-02-08T21:22:32Z **Commit 6:** [courier/fetch/request] remove unneceessary !! * Original sha: eb5446d * Authored by spalger <[email protected]> on 2017-02-09T20:48:48Z
…10274) Backports PR #10081 **Commit 1:** defer loading visualization saved objects so they can be loaded in a single _mget * Original sha: 5c1344e * Authored by nreese <[email protected]> on 2017-01-24T23:41:05Z **Commit 2:** Merge branch 'defer' of https://github.com/nreese/kibana into nreese-defer * Original sha: 96af3ea * Authored by Stacey Gammon <[email protected]> on 2017-01-26T13:44:55Z **Commit 3:** Don't request field stats more than once for the same index pattern * Original sha: 9a02e5b * Authored by Stacey Gammon <[email protected]> on 2017-01-26T15:57:34Z **Commit 4:** [ui/courier] batch fetch requests for all searches and docs * Original sha: 20d55fe * Authored by spalger <[email protected]> on 2017-02-06T22:13:23Z **Commit 5:** [ui/courier] remove remaining mentions of req.isFetchRequested() * Original sha: f5bd5ca * Authored by spalger <[email protected]> on 2017-02-08T21:22:32Z **Commit 6:** [courier/fetch/request] remove unneceessary !! * Original sha: eb5446d * Authored by spalger <[email protected]> on 2017-02-09T20:48:48Z
Took @nreese's PR (#10050) and added some additional logic to avoid multiple field_stat requests.
Here is my finding after playing around with loading times of a dashboard with 30 visualizations (note: all are from the same index, requiring only a single field_stats lookup).
Total # of runs: 10
Total # of visualizations: 30
So it appears that this implementation will shave off about a second of page load time. I suspect that number would grow for slow connection speeds.