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

Batch mget requests on dashboard load and cache field_stat results #10081

Merged
merged 6 commits into from
Feb 9, 2017

Conversation

stacey-gammon
Copy link
Contributor

@stacey-gammon stacey-gammon commented Jan 26, 2017

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

Variation total requests Avg page load lowest highest
Current 87 5.928s 5.6s 6.65s
one mget 58 5.794s 5.37s 6.29s
one mget and one field stat 29 4.95s 4.8s 5.3s

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.

@trevan
Copy link
Contributor

trevan commented Jan 26, 2017

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.

@stacey-gammon
Copy link
Contributor Author

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?

@trevan
Copy link
Contributor

trevan commented Jan 26, 2017

@stacey-gammon, I already had extremely similar logic to what you have and found it very helpful.

spalger
spalger previously requested changes Feb 3, 2017
Copy link
Contributor

@spalger spalger left a 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) => {
Copy link
Contributor

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 how docSource.fetch() functions?

Copy link
Contributor

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.

Copy link
Contributor

@spalger spalger Feb 3, 2017

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...

Copy link
Contributor

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()
  })
}

Copy link
Contributor Author

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 = {};
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@spalger spalger dismissed their stale review February 8, 2017 19:12

Just pushed updates, not really qualified to review anymore

@spalger
Copy link
Contributor

spalger commented Feb 8, 2017

@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 DataSource#fetch() batch by a tiny amount, rather than waiting for the next tick of the looper (applies to data and search sources). Mind taking a look?

@pickypg another review from you would be helpful too

else return fetchThese(requests);
}
const debouncedFetchThese = _.debounce(() => {
const requests = requestQueue.get().filter(req => req.isFetchRequested());
Copy link
Contributor Author

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?

Copy link
Contributor

@spalger spalger Feb 8, 2017

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.

Copy link
Contributor Author

@stacey-gammon stacey-gammon left a 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;
Copy link
Contributor Author

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. :)

Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member

@pickypg pickypg left a comment

Choose a reason for hiding this comment

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

LGTM

@spalger spalger merged commit f2da2a3 into elastic:master Feb 9, 2017
elastic-jasper added a commit that referenced this pull request Feb 9, 2017
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
elastic-jasper added a commit that referenced this pull request Feb 9, 2017
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
epixa pushed a commit that referenced this pull request Feb 9, 2017
…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
epixa pushed a commit that referenced this pull request Feb 9, 2017
…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
@stacey-gammon stacey-gammon deleted the nreese-defer branch April 6, 2017 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants