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

feat(cancel-query-request): added abort query request functionality after submitting a query #18387

Merged
merged 7 commits into from
Jun 16, 2020

Conversation

asalem1
Copy link
Contributor

@asalem1 asalem1 commented Jun 5, 2020

Closes:

#18296

Problem

This PR aims to address two issues:

  1. The perception of slow click events for submitting a query
  2. Users don't have the option to cancel their query requests once they've been initiated

Solution

  1. Created a custom thunk that will set the loading state when the click event is triggered
  2. By passing in a reference to the AbortController with each request within the component, we are easily able to abort the HTTP request. This will abort the query where it lies, and will prevent any further costs from being incurred based on a user's query duration.

cancellation-in-progress

  • CHANGELOG.md updated with a link to the PR (not the Issue)
  • Rebased/mergeable

@asalem1 asalem1 force-pushed the feat/cancel-query-btn branch 3 times, most recently from 2a3b476 to a585d98 Compare June 9, 2020 16:47
})
const results = await Promise.all(pendingResults.map(r => r.promise)).catch(
error => {
Copy link
Contributor

@ebb-tide ebb-tide Jun 9, 2020

Choose a reason for hiding this comment

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

I think if we are catching and then throwing error, it's the same as not catching at all? maybe this can just be the await Promise.all? not sure.

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's a great question, you could be right. Let me go ahead and give that another shot. I think I originally added it to help the error bubble out of the promise, but it could be that I had an issue in the catch at the end of all this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, you're totally right. I will ✂️ it out

@asalem1 asalem1 requested a review from ebb-tide June 9, 2020 21:11

private handleCancelClick = (): void => {
this.props.onNotify(queryCancelRequest())
this.abortController.abort()
Copy link
Contributor

Choose a reason for hiding this comment

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

just to be safe, it's probably best to check that abortController exists, since this function might be called before handleClick when abortController is undefined

Suggested change
this.abortController.abort()
if (this.abortController) {
this.abortController.abort()
}

@@ -23,7 +23,7 @@ module.exports = {
'ts-jest': {
tsConfig: 'tsconfig.test.json',
diagnostics: {
ignoreCodes: [6133, 6192] // ignore unused variable errors
ignoreCodes: [6133, 6192, 2339], // ignore unused variable errors
Copy link
Contributor

Choose a reason for hiding this comment

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

cut this part - we added it for testing

setQueryToLoading: () => void
}

export const DEFAULT_SUBMIT_BTN_CONTEXT: SubmitQueryButtonContextType = {
Copy link
Contributor

Choose a reason for hiding this comment

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

does this need to be exported as a constant (doesn't look like it's used outside this file)? can this just be left here and named in camelCase?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing. I was going based on the previous convention but you're right in that it doesn't need to be exported

@@ -51,16 +52,25 @@ export const runQuery = (
dialect: {annotations: ['group', 'datatype', 'default']},
}

const controller = new AbortController()
const controller = abortController || new AbortController()
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 you should cut this since it's duplicated in runQuery and the result from there is passed in here. caveat emptor to anyone using this method - they'll need to pass in an abort controller - that's fine, since you're the only one using it right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good eyes, I totally didn't see that. Will do

icon={icon}
size={ComponentSize.Small}
status={ComponentStatus.Default}
onClick={() => this.handleCancelClick()}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
onClick={() => this.handleCancelClick()}
onClick={this.handleCancelClick}

@asalem1 asalem1 requested a review from hoorayimhelping June 10, 2020 12:11
@asalem1 asalem1 force-pushed the feat/cancel-query-btn branch from df60034 to eb1d918 Compare June 11, 2020 17:26
@hoorayimhelping hoorayimhelping force-pushed the feat/cancel-query-btn branch 13 times, most recently from b9152fd to 649297a Compare June 15, 2020 15:29
@asalem1 asalem1 force-pushed the feat/cancel-query-btn branch from 649297a to 419a9e5 Compare June 15, 2020 16:40
@hoorayimhelping hoorayimhelping force-pushed the feat/cancel-query-btn branch from 419a9e5 to 649297a Compare June 15, 2020 16:47
@@ -21,6 +21,10 @@ export interface QueryContextType {
query: (text: string) => Promise<BothResults>
}

export interface QueryContextType {
Copy link
Contributor

Choose a reason for hiding this comment

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

what is the difference between this and the one on line 20?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Literally nothing. Good eyes, looks like I added something that shouldn't have been added

@@ -106,7 +106,14 @@ const isFromBucket = (node: Node) => {
)
}

export const executeQueries = () => async (dispatch, getState: GetState) => {
export const setQueryToLoading = () => dispatch => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why this is a thunk and not just an action?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Totally can be. Good call

if (e.name === 'CancellationError') {
return results
} catch (error) {
if (error.name === 'CancellationError' || error.name === 'AbortError') {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

Choose a reason for hiding this comment

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

This showed up cause of testing! There are situations where the request can be canceled before the other the catch in runQuery has a chance to catch it, so it would wind up here without properly being converted into a CancellationError

private handleCancelClick = (): void => {
this.props.onNotify(queryCancelRequest())
if (this.abortController) {
this.abortController.abort()
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder what happens to abortController after you abort it? I'm wondering if you should be setting abortController to null here as well. not sure.

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's a great question. Let me find out

Copy link
Contributor

Choose a reason for hiding this comment

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

oooh great question! 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could be wrong, but according to the docs it looks like once an abort controller is aborted, a rejected promise is returned? Although I'm not entirely certain what the means for the instantiated abortController:

https://dom.spec.whatwg.org/#abortcontroller-api-integration

I don't see why setting the value to null would hurt

dispatch(saveDraftQueries())
dispatch(executeQueries())
Copy link
Contributor

Choose a reason for hiding this comment

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

could you dispatch(setQueryToLoading) here instead of as part of the private handleClick = (): void => { method in submit button?

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 sounds great!

@asalem1 asalem1 force-pushed the feat/cancel-query-btn branch from 649297a to a1132df Compare June 15, 2020 20:35
@asalem1 asalem1 force-pushed the feat/cancel-query-btn branch from a1132df to 597c50f Compare June 15, 2020 20:43
@asalem1 asalem1 requested a review from drdelambre June 15, 2020 20:53
Copy link
Contributor

@drdelambre drdelambre left a comment

Choose a reason for hiding this comment

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

this is much cleaner

@asalem1 asalem1 requested a review from ebb-tide June 15, 2020 22:18
@@ -1,4 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
Copy link
Contributor

Choose a reason for hiding this comment

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

hrrm wait why is this being generated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ebb-tide i removed since I had apparently introduced it by running yarn install at the root directory 7 months ago. Just came across it when I was working in fixing the issue that stemmed from the lock file and figured it should be removed to reduce confusion

@asalem1 asalem1 merged commit b1f7d03 into master Jun 16, 2020
@asalem1 asalem1 deleted the feat/cancel-query-btn branch June 16, 2020 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants