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

Kibana should allow ES request cancellation when browser disconnects #44302

Closed
joshdover opened this issue Aug 28, 2019 · 13 comments · Fixed by #55061
Closed

Kibana should allow ES request cancellation when browser disconnects #44302

joshdover opened this issue Aug 28, 2019 · 13 comments · Fixed by #55061
Assignees
Labels
blocker Feature:New Platform Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc

Comments

@joshdover
Copy link
Contributor

When a browser request disconnects, we should cancel any pending requests to Elasticsearch that the browser request started. This could be done via the ScopedClient class that has access to the raw server request.

This would eliminate wasted Elasticsearch CPU cycles for results that will never be used.

Doing this at the global ElasticsearchService level would allows us to have this behavior by default for all Elasticsearch requests, but we should also provide an escape hatch to opt-out of this behavior for a given request.

Example

router.get(
  { path: 'my-route/', validation },
  async (context, req, res) => {
    const { dataClient } = context.core.elasticsearch;
    
    // This request would automatically cancel if the browser
    // disconnects from Kibana early
    const results = await dataClient.callForCurrentUser('search', query);
    
    // This request would not automatically cancel
    const results = await dataClient.callForCurrentUser('search', query, {
      cancelOnDisconnect: false
    });
  }
);

Related: #42764

@joshdover joshdover added Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc Feature:New Platform labels Aug 28, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-platform

@joshdover joshdover changed the title Scoped Elasticsearch client should automatically cancel ES requests browser disconnects Scoped Elasticsearch client should automatically cancel ES requests when browser disconnects Aug 28, 2019
@stacey-gammon
Copy link
Contributor

fwiw, I think this will block the new data access services from being in NP and replacing courier (#29215).

@joshdover
Copy link
Contributor Author

We discussed this today during the Platform Team's sync and now we think that, yes, we should build this feature into the core ElasticsearchService, but we'd like make it an opt-in behavior.

The example above would now be:

router.get(
  { path: 'my-route/', validation },
  async (context, req, res) => {
    const { dataClient } = context.core.elasticsearch;
    
    // This request would not automatically cancel
    const results = await dataClient.callForCurrentUser('search', query);
    
    // This request would automatically cancel if the browser
    // disconnects from Kibana early
    const results = await dataClient.callForCurrentUser('search', query, {
      cancelOnDisconnect: true
    });
  }
);

By doing this opt-in instead of opt-out, we can introduce this behavior in a lower-risk way and adopt it in pieces across the different query services.

The reason we still want to do this in the core service itself rather than in plugin code, is because we don't want to expose request events to plugins at this time. This is the only known usage of request events, so it does not seem appropriate to expose the request event interface outside of core.

@stacey-gammon Do you have any thoughts or objections?

@stacey-gammon
Copy link
Contributor

that sgtm, I think we can work with that and pass the setting on to users of the search service if they wish to override, while we default to setting it on.

@mshustov
Copy link
Contributor

mshustov commented Jan 8, 2020

The reason we still want to do this in the core service itself rather than in plugin code, is because we don't want to expose request events to plugins at this time. This is the only known usage of request events, so it does not seem appropriate to expose the request event interface outside of core.

after thinking more on the proposed solution:

// This request would not automatically cancel
const results = await dataClient.callForCurrentUser('search', query, {
  cancelOnDisconnect: false
});

I see several problems with it:

  • API inconsistency between request handler context and elasticsearch service contract
  • necessity to handle several abort signals
const results = await dataClient.callForCurrentUser('search', query, {
  signal: abortController.signal, <--- user still can pass signal manually
  cancelOnDisconnect: false
});

Probably we should consider adding nodejs request events to KibanaRequest? There is abort event as well https://nodejs.org/api/http.html#http_event_abort

// KibanaRequest
import { IncomingMessage } from 'http';
class KibanaRequest {
  public readonly on: IncomingMessage['on'];
}
// Plugin code
router.get({ path: '/', validate: false }, async (context, req, res) => {
  const abortController = new AbortController();
  const { signal } = abortController;

  req.on('aborted', abortController.abort);

  const data = await context.core.elasticsearch.dataClient.callAsCurrentUser('ping', {}, { signal });

  return res.ok({ body: data });
});

lizozom pushed a commit to lizozom/kibana that referenced this issue Jan 13, 2020
@joshdover
Copy link
Contributor Author

Probably we should consider adding nodejs request events to KibanaRequest? There is abort event as well nodejs.org/api/http.html#http_event_abort

I'm ok with this, but I do think that we should expose this as an Observable to be consistent with other Platform APIs. AFAIK, we don't have any event listener-style APIs currently.

@joshdover joshdover removed their assignment Jan 13, 2020
@lukasolson
Copy link
Member

@joshdover Are you thinking there would be a separate observable for each type of event? Or would there be a single observable for all events?

@joshdover
Copy link
Contributor Author

Separate might be more ergonomic, though honestly it'd be trivial to expose both.

@pgayvallet
Copy link
Contributor

pgayvallet commented Jan 14, 2020

Probably we should consider adding nodejs request events to KibanaRequest? There is abort event as well https://nodejs.org/api/http.html#http_event_abort

@joshdover Are you thinking there would be a separate observable for each type of event? Or would there be a single observable for all events?

Is there any need to expose any other event/observable that abort ATM? For consistency, I also think it should be expose either as a observable or a a promise. Using a promise for ES calls is probably more elegant/logic though, as this should/can only emit once.

WDYT of

// KibanaRequest
class KibanaRequest {
  public readonly aborted$: Observable<void>
}
// Plugin code
router.get({ path: '/', validate: false }, async (context, req, res) => {

  const data = await context.core.elasticsearch.dataClient.callAsCurrentUser(
    'ping', 
    {}, 
    { abort: req.aborted$.toPromise() }
  );

  return res.ok({ body: data });
});

We could even add some sugar by just passing up the request and doing the machinery internally.

// KibanaRequest
class KibanaRequest {
  public readonly aborted$: Observable<void>
}
// Plugin code
router.get({ path: '/', validate: false }, async (context, req, res) => {

  const data = await context.core.elasticsearch.dataClient.callAsCurrentUser(
    'ping', 
    {}, 
    { request: req }
  );

  return res.ok({ body: data });
});

@lukasolson
Copy link
Member

Would it look something like this? https://github.com/elastic/kibana/compare/master...lukasolson:abort?expand=1

If platform team doesn't have the resources to implement this, I'm sure either @lizozom or I would be willing to tackle this as it seems like it might be low fruit.

@joshdover
Copy link
Contributor Author

joshdover commented Jan 14, 2020 via email

@mshustov
Copy link
Contributor

mshustov commented Jan 16, 2020

started working on the issue.

We could even add some sugar by just passing up the request and doing the machinery internally.

This is similar to proposed solution #44302 (comment) with drawbacks mentioned in #44302 (comment)

@mshustov mshustov changed the title Scoped Elasticsearch client should automatically cancel ES requests when browser disconnects Elasticsearch client should allow ES request cancellation when browser disconnects Jan 16, 2020
@pgayvallet
Copy link
Contributor

This is similar to proposed solution #44302 (comment) with drawbacks mentioned in #44302 (comment)

You are right, forget the dataClient was coming from the handler context and therefor could already be aware of the request implicitly.

@mshustov mshustov changed the title Elasticsearch client should allow ES request cancellation when browser disconnects Kibana should allow ES request cancellation when browser disconnects Jan 16, 2020
lizozom pushed a commit that referenced this issue Jan 21, 2020
* deprecate msearch

* Missing export

* adjust tests, revert loading method of esaggs/boot

* getInjectedMetadata

* Fix jest tests

* update default strategy abort test

* notice update

* Allow running discover errors test independently

* Remove batchSearches

* Detect painless script error

* don't show notifications for aborted requests

* Fix jest tests

* Restore loader indicator

* Decreace loading count on error

* update search test

* Trigger digest after fetching fresh index patterns

* Revert isEqual

* accurate revert

* Return full error details to client from search endpoint

* Re-throw AbortError from http when user aborts request.

* fix typo

* typo

* Adjust routes jest test

* Restore msearch using a separate es connection

* typescript fixes

* set http service mock

* Move es client to dat aplugin, for follow up PR

* Add karma mock

* krma mock

* fix tests

* ts

* Pass in version dynamically

* add headers to esClient host

* Restored fetch soon test
Use tap for loadingCount side effects

* Cleanup search params

* Cleanup search params test

* Revert "Cleanup search params"

This reverts commit ca9dea0.

* Revert "Cleanup search params test"

This reverts commit 30b9478.

* Revert code to use old es client until  #44302 is resolved

* Revert changes to getPainlessError

* Fix jest test

* Refactor esClient to trigger loadingIndicator

* fixing tests

* use esClient from searchService

* git remove comment

* fix jest

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
lizozom pushed a commit to lizozom/kibana that referenced this issue Jan 21, 2020
* deprecate msearch

* Missing export

* adjust tests, revert loading method of esaggs/boot

* getInjectedMetadata

* Fix jest tests

* update default strategy abort test

* notice update

* Allow running discover errors test independently

* Remove batchSearches

* Detect painless script error

* don't show notifications for aborted requests

* Fix jest tests

* Restore loader indicator

* Decreace loading count on error

* update search test

* Trigger digest after fetching fresh index patterns

* Revert isEqual

* accurate revert

* Return full error details to client from search endpoint

* Re-throw AbortError from http when user aborts request.

* fix typo

* typo

* Adjust routes jest test

* Restore msearch using a separate es connection

* typescript fixes

* set http service mock

* Move es client to dat aplugin, for follow up PR

* Add karma mock

* krma mock

* fix tests

* ts

* Pass in version dynamically

* add headers to esClient host

* Restored fetch soon test
Use tap for loadingCount side effects

* Cleanup search params

* Cleanup search params test

* Revert "Cleanup search params"

This reverts commit ca9dea0.

* Revert "Cleanup search params test"

This reverts commit 30b9478.

* Revert code to use old es client until  elastic#44302 is resolved

* Revert changes to getPainlessError

* Fix jest test

* Refactor esClient to trigger loadingIndicator

* fixing tests

* use esClient from searchService

* git remove comment

* fix jest

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
lizozom pushed a commit that referenced this issue Jan 21, 2020
* deprecate msearch

* Missing export

* adjust tests, revert loading method of esaggs/boot

* getInjectedMetadata

* Fix jest tests

* update default strategy abort test

* notice update

* Allow running discover errors test independently

* Remove batchSearches

* Detect painless script error

* don't show notifications for aborted requests

* Fix jest tests

* Restore loader indicator

* Decreace loading count on error

* update search test

* Trigger digest after fetching fresh index patterns

* Revert isEqual

* accurate revert

* Return full error details to client from search endpoint

* Re-throw AbortError from http when user aborts request.

* fix typo

* typo

* Adjust routes jest test

* Restore msearch using a separate es connection

* typescript fixes

* set http service mock

* Move es client to dat aplugin, for follow up PR

* Add karma mock

* krma mock

* fix tests

* ts

* Pass in version dynamically

* add headers to esClient host

* Restored fetch soon test
Use tap for loadingCount side effects

* Cleanup search params

* Cleanup search params test

* Revert "Cleanup search params"

This reverts commit ca9dea0.

* Revert "Cleanup search params test"

This reverts commit 30b9478.

* Revert code to use old es client until  #44302 is resolved

* Revert changes to getPainlessError

* Fix jest test

* Refactor esClient to trigger loadingIndicator

* fixing tests

* use esClient from searchService

* git remove comment

* fix jest

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocker Feature:New Platform Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants