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

TDL-15860 Implement Request Timeouts #79

Merged
merged 20 commits into from
Dec 7, 2021

Conversation

prijendev
Copy link
Contributor

@prijendev prijendev commented Oct 19, 2021

Description of change

  • Implemented request timeout.
  • Added unittest case for the same.
  • If config parameter does not pass or pass the value "", "0", 0, "0.0" then set timeout to default value 5min

Note :

  • Get call in get_incremental_export is replaced by call_api which is covered in PR#74. So, once that PR merged, timeout changes will covered.
  • Disabled 'consider-using-ternary' pylint error in code.

Manual QA steps

  • Check that tap retry 10 times on timeout error.

Risks

Rollback steps

  • revert this branch

@prijendev prijendev changed the title Implemented request timeout TDL-15860 Implement Request Timeouts Oct 19, 2021
@@ -19,7 +19,8 @@ OAuth is the default authentication method for `tap-zendesk`. To use OAuth, you
{
"access_token": "AVERYLONGOAUTHTOKEN",
"subdomain": "acme",
"start_date": "2000-01-01T00:00:00Z"
"start_date": "2000-01-01T00:00:00Z",
"request_timeout": 300
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add details of it as it is the optional parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added.

- run:
name: 'pylint'
command: |
source /usr/local/share/virtualenvs/tap-zendesk/bin/activate
make test
pylint tap_zendesk -d missing-docstring,invalid-name,line-too-long,too-many-locals,too-few-public-methods,fixme,stop-iteration-return,too-many-branches,useless-import-alias,no-else-return,logging-not-lazy
Copy link
Contributor

Choose a reason for hiding this comment

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

@prijendev Did you go through the errors returned by pylint which you have skipped intentionally?
Is it something that cannot be resolved?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

make test command runs both pylint and nosetests. But now as we are adding code coverage with unittests we need to change nosetests with some extra argument. So, it's just spllited make test command. We have not changed existing pylint command. Existing command can be seen under pylint section of circleci

# Set request timeout to config param `request_timeout` value.
# If value is 0,"0","" or not passed then it set default to 300 seconds.
config_request_timeout = parsed_args.config.get('request_timeout')
request_timeout = config_request_timeout and float(config_request_timeout) or REQUEST_TIMEOUT # pylint: disable=consider-using-ternary
Copy link
Contributor

Choose a reason for hiding this comment

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

@prijendev Please update this condition as follows for better visibility and understanding
request_timeout = (config_request_timeout and float(config_request_timeout)) or REQUEST_TIMEOUT

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

response = requests.get(url, params=params, headers=headers)
@backoff.on_exception(backoff.expo,Timeout, #As timeout error does not have attribute status_code, hence giveup does not work in this case.
max_tries=10) # So, here we added another backoff expression.
def call_api(url, request_timeout, params, headers):
Copy link
Contributor

Choose a reason for hiding this comment

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

@prijendev I see all the calls to function call_api is updated due to addition of a single parameter in the function argument.
My suggestion:
Since request_timeout is initialized in the main() function, can't we directly utilize it in the call_api function instead of passing it as an argument? The benefit of this change will be, changes to call_api function can be removed. Minimal changes will be resulted in the PR. Code would be more robust and generic.

CC: @dbshah1212

Copy link
Contributor Author

@prijendev prijendev Nov 1, 2021

Choose a reason for hiding this comment

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

In this tap some of the streams using Zenpy sdk. So, to initialize sdk client we have read request_timeout in main() function. While for other streams each sync method using call_api function. Updated initialization of request_timeout to common parent Stream class to minimize the changes.

for page in http.get_cursor_based(url, self.config['access_token'], **kwargs):
# Set and pass request timeout to config param `request_timeout` value.
# If value is 0,"0","" or not passed then it set default to 300 seconds.
config_request_timeout = self.config.get('request_timeout')
Copy link
Contributor

Choose a reason for hiding this comment

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

@prijendev This code is already written in the main() function of init file. What is the need of initializing these variables again? Configuration parameters should be read only once in the starting of the tap.

Copy link
Contributor Author

@prijendev prijendev Nov 1, 2021

Choose a reason for hiding this comment

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

In this tap some of the streams using Zenpy sdk. So, to initialize sdk client we have read request_timeout in main() function. While for other streams updated initialization of request_timeout to common parent Stream class to minimize the changes. So, overall we require to read request_timeout 2 times.

for page in http.get_incremental_export(url, self.config['access_token'], start_time):
# Set and pass request timeout to config param `request_timeout` value.
# If value is 0,"0","" or not passed then it set default to 300 seconds.
config_request_timeout = self.config.get('request_timeout')
Copy link
Contributor

Choose a reason for hiding this comment

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

@prijendev Same comment here as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this tap some of the streams using Zenpy sdk. So, to initialize sdk client we have read request_timeout in main() function. While for other streams updated initialization of request_timeout to common parent Stream class to minimize the changes. So, overall we require to read request_timeout 2 times.

@@ -13,6 +13,7 @@
'after_cursor': 'some_cursor'}
Copy link
Contributor

Choose a reason for hiding this comment

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

@prijendev Did we write test cases for different scenarios of request_timeout variable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, We write test cases for different scenarios of request_timeout variable in test_request_timeout.py file.

# If value is 0,"0","" or not passed then it set default to 300 seconds.
config_request_timeout = self.config.get('request_timeout')
request_timeout = config_request_timeout and float(config_request_timeout) or REQUEST_TIMEOUT # pylint: disable=consider-using-ternary
pages = http.get_offset_based(url, self.config['access_token'], request_timeout)
Copy link
Contributor

Choose a reason for hiding this comment

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

Did we write test cases for this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we write test cases for this change in test_request_timeout.py. Method name - test_ticket_metrics_timeout_error_with_empty_value

# Set and pass request timeout to config param `request_timeout` value.
# If value is 0,"0","" or not passed then it set default to 300 seconds.
config_request_timeout = self.config.get('request_timeout')
self.request_timeout = (config_request_timeout and float(config_request_timeout)) or REQUEST_TIMEOUT # pylint: disable=consider-using-ternary
Copy link
Contributor

Choose a reason for hiding this comment

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

@prijendev We should figure out an alternative to resolve the pylint error. Why to disable it completely?
Can you research on what can be the alternative ways to assign values to self.request_timeout?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed disable of pylint error. Updated code.

@@ -22,12 +22,14 @@ def is_fatal(exception):
requests.exceptions.HTTPError,
max_tries=10,
giveup=is_fatal)
def call_api(url, params, headers):
response = requests.get(url, params=params, headers=headers)
@backoff.on_exception(backoff.expo,Timeout, #As timeout error does not have attribute status_code, hence giveup does not work in this case.
Copy link
Contributor

Choose a reason for hiding this comment

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

We are adding max_tries=5 with factor=2 so please update accordingly to give significant delay between 2 retries.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated it.

if config_request_timeout and float(config_request_timeout):
request_timeout = float(config_request_timeout)
else:
request_timeout = REQUEST_TIMEOUT # If value is 0,"0","" or not passed then it set default to 300 seconds.
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
request_timeout = REQUEST_TIMEOUT # If value is 0,"0","" or not passed then it set default to 300 seconds.
request_timeout = REQUEST_TIMEOUT # If value is 0, "0", "" or not passed then it sets default to 300 seconds.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

@KrisPersonal KrisPersonal merged commit 2172f2a into master Dec 7, 2021
@KrisPersonal KrisPersonal deleted the TDL-15860-Implement-Request-Timeout branch December 7, 2021 18:41
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.

5 participants