From b2d8c91001d0bcc7772488205710795f9d7b2a7c Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Fri, 17 Jan 2020 16:34:34 +0530 Subject: [PATCH 01/23] tested --- bin/qds.py | 24 ++++++++++++++++++++++-- qds_sdk/connection.py | 40 +++++++++++++++++++++++++++++++++------- qds_sdk/qubole.py | 31 ++++++++++++++++++++----------- qds_sdk/retry.py | 24 ------------------------ 4 files changed, 75 insertions(+), 44 deletions(-) delete mode 100644 qds_sdk/retry.py diff --git a/bin/qds.py b/bin/qds.py index fac8b439..700fb4a1 100755 --- a/bin/qds.py +++ b/bin/qds.py @@ -582,6 +582,17 @@ def main(): default=os.getenv('CLOUD_PROVIDER'), help="cloud", choices=["AWS", "AZURE", "ORACLE_BMC", "ORACLE_OPC", "GCP"]) + optparser.add_option("--retry_delay", dest="retry_delay", + type=int, + default=os.getenv('QDS_RETRY_DELAY'), + help="sleep interval between successive retries in case of retryable exceptions. defaults to 30s." + ) + optparser.add_option("--max_retries", dest="max_retries", + type=int, + default=os.getenv('QDS_MAX_RETRIES'), + help="Number of re-attempts for an api-call in case of retryable exceptions. defaults to 6." + ) + optparser.add_option("-v", dest="verbose", action="store_true", default=False, help="verbose mode - info level logging") @@ -613,6 +624,12 @@ def main(): if options.poll_interval is None: options.poll_interval = 5 + if options.max_retries is None: + options.max_retries = 6 + + if options.retry_delay is None: + options.retry_delay = 30 + if options.cloud_name is None: options.cloud_name = "AWS" @@ -626,7 +643,10 @@ def main(): version=options.api_version, poll_interval=options.poll_interval, skip_ssl_cert_check=options.skip_ssl_cert_check, - cloud_name=options.cloud_name) + cloud_name=options.cloud_name, + retry_delay=options.retry_delay, + max_retries=options.max_retries + ) if len(args) < 1: sys.stderr.write("Missing first argument containing subcommand\n") @@ -699,4 +719,4 @@ def main(): sys.exit(2) except Exception: traceback.print_exc(file=sys.stderr) - sys.exit(3) + sys.exit(3) \ No newline at end of file diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index dbec341b..32444870 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -3,6 +3,7 @@ import logging import ssl import json +import time import pkg_resources from requests.adapters import HTTPAdapter from datetime import datetime @@ -10,8 +11,8 @@ from requests.packages.urllib3.poolmanager import PoolManager except ImportError: from urllib3.poolmanager import PoolManager -from qds_sdk.retry import retry from qds_sdk.exception import * +from functools import wraps log = logging.getLogger("qds_connection") @@ -34,7 +35,7 @@ def init_poolmanager(self, connections, maxsize,block=False): class Connection: - def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True): + def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=5, retry_delay=30): self.auth = auth self.rest_url = rest_url self.skip_ssl_cert_check = skip_ssl_cert_check @@ -42,6 +43,8 @@ def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True): 'Content-Type': 'application/json'} self.reuse = reuse + self.max_retries = max_retries + self.retry_delay = retry_delay if reuse: self.session = requests.Session() self.session.mount('https://', MyAdapter()) @@ -50,20 +53,42 @@ def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True): self.session_with_retries = requests.Session() self.session_with_retries.mount('https://', MyAdapter(max_retries=3)) - @retry((RetryWithDelay, requests.Timeout, ServerError), tries=6, delay=30, backoff=2) + def retry(ExceptionToCheck, tries=4, delay=3, backoff=2): + def deco_retry(f): + @wraps(f) + def f_retry(self,*args, **kwargs): + mtries, mdelay = self.max_retries, self.retry_delay + while mtries > 1: + try: + return f(self,*args, **kwargs) + except ExceptionToCheck as e: + logger = logging.getLogger("retry") + msg = "%s, Retrying in %d seconds..." % (e.__class__.__name__, mdelay) + logger.info(msg) + time.sleep(mdelay) + mtries -= 1 + mdelay *= backoff + return f(self,*args, **kwargs) + return f_retry # true decorator + return deco_retry + + @retry((RetryWithDelay, requests.Timeout)) def get_raw(self, path, params=None): return self._api_call_raw("GET", path, params=params) - @retry((RetryWithDelay, requests.Timeout, ServerError), tries=6, delay=30, backoff=2) + @retry((RetryWithDelay, requests.Timeout)) def get(self, path, params=None): return self._api_call("GET", path, params=params) + @retry((RetryWithDelay, requests.Timeout)) def put(self, path, data=None): return self._api_call("PUT", path, data) + @retry((RetryWithDelay, requests.Timeout)) def post(self, path, data=None): return self._api_call("POST", path, data) + @retry((RetryWithDelay, requests.Timeout)) def delete(self, path, data=None): return self._api_call("DELETE", path, data) @@ -111,10 +136,8 @@ def _api_call(self, req_type, path, data=None, params=None): @staticmethod def _handle_error(response): """Raise exceptions in response to any http errors - Args: response: A Response object - Raises: BadRequest: if HTTP error code 400 returned. UnauthorizedAccess: if HTTP error code 401 returned. @@ -165,6 +188,9 @@ def _handle_error(response): elif code == 449: sys.stderr.write(response.text + "\n") raise RetryWithDelay(response, "Data requested is unavailable. Retrying ...") + elif code == 429: + sys.stderr.write(response.text + "\n") + raise RetryWithDelay(response) elif 401 <= code < 500: sys.stderr.write(response.text + "\n") raise ClientError(response) @@ -181,4 +207,4 @@ def _validate_json(response): response.json() except Exception as e: sys.stderr.write("Error: {0}\nInvalid Response from Server, please contact Qubole Support".format(str(e))) - raise ServerError(response) + raise ServerError(response) \ No newline at end of file diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index d8b842a5..be5ae47e 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -21,6 +21,8 @@ class Qubole: """ MIN_POLL_INTERVAL = 1 + RETRIES_CAP = 10 + MAX_DELAY = 30 _auth = None api_token = None @@ -31,22 +33,22 @@ class Qubole: cloud_name = None cached_agent = None cloud = None + retry_delay = None + max_retries = None @classmethod def configure(cls, api_token, api_url="https://api.qubole.com/api/", version="v1.2", - poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS"): + poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", retry_delay=30, max_retries=6): """ Set parameters governing interaction with QDS - Args: `api_token`: authorization token for QDS. required - `api_url`: the base URL for QDS API. configurable for testing only - `version`: QDS REST api version. Will be used throughout unless overridden in Qubole.agent(..) - `poll_interval`: interval in secs when polling QDS for events + `delay` : interval in secs to sleep in between successive retries + `retries` : maximum number of time to retry an api call in case of retryable exception. """ cls._auth = QuboleAuth(api_token) @@ -61,14 +63,22 @@ def configure(cls, api_token, cls.skip_ssl_cert_check = skip_ssl_cert_check cls.cloud_name = cloud_name.lower() cls.cached_agent = None - + if retry_delay > Qubole.MAX_DELAY: + log.warn("Sleep between successive retries cannot be greater than %s seconds. Setting it to %s seconds.\n" % (Qubole.MAX_DELAY, Qubole.MAX_DELAY)) + cls.retry_delay = Qubole.MAX_DELAY + else: + cls.retry_delay = retry_delay + if max_retries > Qubole.RETRIES_CAP: + log.warn("Maximum retries cannot be greater than %s . Setting it to %s .\n" % (Qubole.RETRIES_CAP, 6)) + cls.max_retries = 6 + else: + cls.max_retries = max_retries @classmethod def agent(cls, version=None): """ Returns: a connection object to make REST calls to QDS - optionally override the `version` of the REST endpoint for advanced features available only in the newer version of the API available for certain resource end points eg: /v1.3/cluster. When version is @@ -85,10 +95,10 @@ def agent(cls, version=None): raise ConfigError("No API Token specified - please supply one via Qubole.configure()") if not reuse_cached_agent: - uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check) + uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, True, cls.max_retries, cls.retry_delay) return uncached_agent if cls.cached_agent is None: - cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check) + cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, True, cls.max_retries, cls.retry_delay) return cls.cached_agent @@ -120,5 +130,4 @@ def get_cloud_object(cls, cloud_name): return qds_sdk.cloud.oracle_opc_cloud.OracleOpcCloud() elif cloud_name.lower() == "gcp": import qds_sdk.cloud.gcp_cloud - return qds_sdk.cloud.gcp_cloud.GcpCloud() - + return qds_sdk.cloud.gcp_cloud.GcpCloud() \ No newline at end of file diff --git a/qds_sdk/retry.py b/qds_sdk/retry.py deleted file mode 100644 index 1676317a..00000000 --- a/qds_sdk/retry.py +++ /dev/null @@ -1,24 +0,0 @@ -import time -import logging -from functools import wraps - -log = logging.getLogger("retry") - - -def retry(ExceptionToCheck, tries=4, delay=3, backoff=2): - def deco_retry(f): - @wraps(f) - def f_retry(*args, **kwargs): - mtries, mdelay = tries, delay - while mtries > 1: - try: - return f(*args, **kwargs) - except ExceptionToCheck as e: - msg = "%s, Retrying in %d seconds..." % (e.__class__.__name__, mdelay) - log.info(msg) - time.sleep(mdelay) - mtries -= 1 - mdelay *= backoff - return f(*args, **kwargs) - return f_retry # true decorator - return deco_retry From 3da5844d46e76827ff13e5191eca36e424c4b40e Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Fri, 17 Jan 2020 16:39:38 +0530 Subject: [PATCH 02/23] nit --- bin/qds.py | 2 +- qds_sdk/connection.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/qds.py b/bin/qds.py index 700fb4a1..ff9e8895 100755 --- a/bin/qds.py +++ b/bin/qds.py @@ -719,4 +719,4 @@ def main(): sys.exit(2) except Exception: traceback.print_exc(file=sys.stderr) - sys.exit(3) \ No newline at end of file + sys.exit(3) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 32444870..80789e00 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -76,7 +76,7 @@ def f_retry(self,*args, **kwargs): def get_raw(self, path, params=None): return self._api_call_raw("GET", path, params=params) - @retry((RetryWithDelay, requests.Timeout)) + @retry((RetryWithDelay, requests.Timeout, ServerError)) def get(self, path, params=None): return self._api_call("GET", path, params=params) @@ -207,4 +207,4 @@ def _validate_json(response): response.json() except Exception as e: sys.stderr.write("Error: {0}\nInvalid Response from Server, please contact Qubole Support".format(str(e))) - raise ServerError(response) \ No newline at end of file + raise ServerError(response) From 75552f5d5481e67d147818f4c22a4d23c714cebb Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 20 Jan 2020 12:28:48 +0530 Subject: [PATCH 03/23] utest fix expected --- qds_sdk/connection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 80789e00..535dfb18 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -57,7 +57,10 @@ def retry(ExceptionToCheck, tries=4, delay=3, backoff=2): def deco_retry(f): @wraps(f) def f_retry(self,*args, **kwargs): - mtries, mdelay = self.max_retries, self.retry_delay + if hasattr(self, 'max_retries'): + mtries, mdelay = self.max_retries, self.retry_delay + else: + mtries, mdelay = tries, delay while mtries > 1: try: return f(self,*args, **kwargs) From 6bf8d5bcff7e07d65162e6ebf72f3254ca336bfc Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 20 Jan 2020 12:41:11 +0530 Subject: [PATCH 04/23] added additional test arguments in Connection-utest-fix-2 --- tests/test_cluster.py | 8 ++++---- tests/test_clusterv2.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_cluster.py b/tests/test_cluster.py index b06e1c08..f08dc4a2 100644 --- a/tests/test_cluster.py +++ b/tests/test_cluster.py @@ -109,7 +109,7 @@ def test_connection(self): Connection.__init__ = Mock(return_value=None) Connection._api_call = Mock(return_value={}) qds.main() - Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.2', ANY) + Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.2', ANY, ANY, ANY, ANY) def test_connection_v13(self): sys.argv = ['qds.py', '--version', 'v1.3', 'cluster', 'list'] @@ -117,7 +117,7 @@ def test_connection_v13(self): Connection.__init__ = Mock(return_value=None) Connection._api_call = Mock(return_value={}) qds.main() - Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.3', ANY) + Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.3', ANY, ANY, ANY, ANY) class TestClusterShow(QdsCliTestCase): @@ -128,7 +128,7 @@ def test_connection(self): Connection.__init__ = Mock(return_value=None) Connection._api_call = Mock(return_value={}) qds.main() - Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.2', ANY) + Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.2', ANY, ANY, ANY, ANY) def test_connection_v13(self): sys.argv = ['qds.py', '--version', 'v1.3', 'cluster', 'list', '--label', 'test_label'] @@ -136,7 +136,7 @@ def test_connection_v13(self): Connection.__init__ = Mock(return_value=None) Connection._api_call = Mock(return_value={}) qds.main() - Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.3', ANY) + Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v1.3', ANY, ANY, ANY, ANY) class TestClusterDelete(QdsCliTestCase): def test_success(self): diff --git a/tests/test_clusterv2.py b/tests/test_clusterv2.py index 0c149e50..aef0f50d 100644 --- a/tests/test_clusterv2.py +++ b/tests/test_clusterv2.py @@ -1050,7 +1050,7 @@ def test_connection(self): Connection.__init__ = Mock(return_value=None) Connection._api_call = Mock(return_value={}) qds.main() - Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v2', ANY) + Connection.__init__.assert_called_with(ANY, 'https://qds.api.url/api/v2', ANY, ANY, ANY, ANY) class TestClusterStatus(QdsCliTestCase): From b809bdd301f55d3989548269a0f5b3271b48ef63 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 20 Jan 2020 15:59:37 +0530 Subject: [PATCH 05/23] configuration values calculated --- qds_sdk/connection.py | 10 +++++----- qds_sdk/qubole.py | 22 +++++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 535dfb18..ec874480 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -35,7 +35,7 @@ def init_poolmanager(self, connections, maxsize,block=False): class Connection: - def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=5, retry_delay=30): + def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=6, retry_delay=15): self.auth = auth self.rest_url = rest_url self.skip_ssl_cert_check = skip_ssl_cert_check @@ -53,17 +53,17 @@ def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries= self.session_with_retries = requests.Session() self.session_with_retries.mount('https://', MyAdapter(max_retries=3)) - def retry(ExceptionToCheck, tries=4, delay=3, backoff=2): + def retry(ExceptionToCheck, tries=6, delay=15, backoff=2): def deco_retry(f): @wraps(f) - def f_retry(self,*args, **kwargs): + def f_retry(self, *args, **kwargs): if hasattr(self, 'max_retries'): mtries, mdelay = self.max_retries, self.retry_delay else: mtries, mdelay = tries, delay while mtries > 1: try: - return f(self,*args, **kwargs) + return f(self, *args, **kwargs) except ExceptionToCheck as e: logger = logging.getLogger("retry") msg = "%s, Retrying in %d seconds..." % (e.__class__.__name__, mdelay) @@ -71,7 +71,7 @@ def f_retry(self,*args, **kwargs): time.sleep(mdelay) mtries -= 1 mdelay *= backoff - return f(self,*args, **kwargs) + return f(self, *args, **kwargs) return f_retry # true decorator return deco_retry diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index be5ae47e..b7f8376c 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -21,8 +21,8 @@ class Qubole: """ MIN_POLL_INTERVAL = 1 - RETRIES_CAP = 10 - MAX_DELAY = 30 + RETRIES_CAP = 6 + MAX_DELAY = 15 _auth = None api_token = None @@ -39,7 +39,7 @@ class Qubole: @classmethod def configure(cls, api_token, api_url="https://api.qubole.com/api/", version="v1.2", - poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", retry_delay=30, max_retries=6): + poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", retry_delay=15, max_retries=6): """ Set parameters governing interaction with QDS Args: @@ -64,13 +64,15 @@ def configure(cls, api_token, cls.cloud_name = cloud_name.lower() cls.cached_agent = None if retry_delay > Qubole.MAX_DELAY: - log.warn("Sleep between successive retries cannot be greater than %s seconds. Setting it to %s seconds.\n" % (Qubole.MAX_DELAY, Qubole.MAX_DELAY)) + log.warn("Sleep between successive retries cannot be greater than %s seconds." + " Setting it to %s seconds.\n" % (Qubole.MAX_DELAY, Qubole.MAX_DELAY)) cls.retry_delay = Qubole.MAX_DELAY else: cls.retry_delay = retry_delay if max_retries > Qubole.RETRIES_CAP: - log.warn("Maximum retries cannot be greater than %s . Setting it to %s .\n" % (Qubole.RETRIES_CAP, 6)) - cls.max_retries = 6 + log.warn("Maximum retries cannot be greater than %s." + " Setting it to %s .\n" % (Qubole.RETRIES_CAP, Qubole.RETRIES_CAP)) + cls.max_retries = Qubole.RETRIES_CAP else: cls.max_retries = max_retries @@ -95,10 +97,12 @@ def agent(cls, version=None): raise ConfigError("No API Token specified - please supply one via Qubole.configure()") if not reuse_cached_agent: - uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, True, cls.max_retries, cls.retry_delay) + uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, + True, cls.max_retries, cls.retry_delay) return uncached_agent if cls.cached_agent is None: - cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, True, cls.max_retries, cls.retry_delay) + cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, + True, cls.max_retries, cls.retry_delay) return cls.cached_agent @@ -130,4 +134,4 @@ def get_cloud_object(cls, cloud_name): return qds_sdk.cloud.oracle_opc_cloud.OracleOpcCloud() elif cloud_name.lower() == "gcp": import qds_sdk.cloud.gcp_cloud - return qds_sdk.cloud.gcp_cloud.GcpCloud() \ No newline at end of file + return qds_sdk.cloud.gcp_cloud.GcpCloud() From 6490070e00b7bea4844eb8b1f04ac7e0e2fc81b6 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 20 Jan 2020 16:00:53 +0530 Subject: [PATCH 06/23] nit --- bin/qds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/qds.py b/bin/qds.py index ff9e8895..cbcdc929 100755 --- a/bin/qds.py +++ b/bin/qds.py @@ -585,7 +585,7 @@ def main(): optparser.add_option("--retry_delay", dest="retry_delay", type=int, default=os.getenv('QDS_RETRY_DELAY'), - help="sleep interval between successive retries in case of retryable exceptions. defaults to 30s." + help="sleep interval between successive retries in case of retryable exceptions. defaults to 15s." ) optparser.add_option("--max_retries", dest="max_retries", type=int, From ac4c8a8657a1faf830a15ddc9d28c54621c70af1 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 20 Jan 2020 16:28:14 +0530 Subject: [PATCH 07/23] modified name and values --- bin/qds.py | 12 ++++++------ qds_sdk/connection.py | 8 ++++---- qds_sdk/qubole.py | 20 ++++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bin/qds.py b/bin/qds.py index cbcdc929..66ec4cee 100755 --- a/bin/qds.py +++ b/bin/qds.py @@ -582,10 +582,10 @@ def main(): default=os.getenv('CLOUD_PROVIDER'), help="cloud", choices=["AWS", "AZURE", "ORACLE_BMC", "ORACLE_OPC", "GCP"]) - optparser.add_option("--retry_delay", dest="retry_delay", + optparser.add_option("--base_retry_delay", dest="base_retry_delay", type=int, - default=os.getenv('QDS_RETRY_DELAY'), - help="sleep interval between successive retries in case of retryable exceptions. defaults to 15s." + default=os.getenv('QDS_BASE_RETRY_DELAY'), + help="base sleep interval for exponential backoff in case of retryable exceptions. defaults to 10s." ) optparser.add_option("--max_retries", dest="max_retries", type=int, @@ -627,8 +627,8 @@ def main(): if options.max_retries is None: options.max_retries = 6 - if options.retry_delay is None: - options.retry_delay = 30 + if options.base_retry_delay is None: + options.base_retry_delay = 30 if options.cloud_name is None: options.cloud_name = "AWS" @@ -644,7 +644,7 @@ def main(): poll_interval=options.poll_interval, skip_ssl_cert_check=options.skip_ssl_cert_check, cloud_name=options.cloud_name, - retry_delay=options.retry_delay, + base_retry_delay=options.base_retry_delay, max_retries=options.max_retries ) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index ec874480..3c9cbaed 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -35,7 +35,7 @@ def init_poolmanager(self, connections, maxsize,block=False): class Connection: - def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=6, retry_delay=15): + def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=6, base_retry_delay=10): self.auth = auth self.rest_url = rest_url self.skip_ssl_cert_check = skip_ssl_cert_check @@ -44,7 +44,7 @@ def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries= self.reuse = reuse self.max_retries = max_retries - self.retry_delay = retry_delay + self.base_retry_delay = base_retry_delay if reuse: self.session = requests.Session() self.session.mount('https://', MyAdapter()) @@ -53,12 +53,12 @@ def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries= self.session_with_retries = requests.Session() self.session_with_retries.mount('https://', MyAdapter(max_retries=3)) - def retry(ExceptionToCheck, tries=6, delay=15, backoff=2): + def retry(ExceptionToCheck, tries=6, delay=10, backoff=2): def deco_retry(f): @wraps(f) def f_retry(self, *args, **kwargs): if hasattr(self, 'max_retries'): - mtries, mdelay = self.max_retries, self.retry_delay + mtries, mdelay = self.max_retries, self.base_retry_delay else: mtries, mdelay = tries, delay while mtries > 1: diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index b7f8376c..ddbafa6f 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -22,7 +22,7 @@ class Qubole: MIN_POLL_INTERVAL = 1 RETRIES_CAP = 6 - MAX_DELAY = 15 + MAX_RETRY_DELAY = 10 _auth = None api_token = None @@ -33,13 +33,13 @@ class Qubole: cloud_name = None cached_agent = None cloud = None - retry_delay = None + base_retry_delay = None max_retries = None @classmethod def configure(cls, api_token, api_url="https://api.qubole.com/api/", version="v1.2", - poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", retry_delay=15, max_retries=6): + poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", base_retry_delay=10, max_retries=6): """ Set parameters governing interaction with QDS Args: @@ -63,15 +63,15 @@ def configure(cls, api_token, cls.skip_ssl_cert_check = skip_ssl_cert_check cls.cloud_name = cloud_name.lower() cls.cached_agent = None - if retry_delay > Qubole.MAX_DELAY: + if base_retry_delay > Qubole.MAX_RETRY_DELAY: log.warn("Sleep between successive retries cannot be greater than %s seconds." - " Setting it to %s seconds.\n" % (Qubole.MAX_DELAY, Qubole.MAX_DELAY)) - cls.retry_delay = Qubole.MAX_DELAY + " Setting it to %s seconds.\n" % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) + cls.base_retry_delay = Qubole.MAX_RETRY_DELAY else: - cls.retry_delay = retry_delay + cls.base_retry_delay = base_retry_delay if max_retries > Qubole.RETRIES_CAP: log.warn("Maximum retries cannot be greater than %s." - " Setting it to %s .\n" % (Qubole.RETRIES_CAP, Qubole.RETRIES_CAP)) + " Setting it to default - %s.\n" % (Qubole.RETRIES_CAP, Qubole.RETRIES_CAP)) cls.max_retries = Qubole.RETRIES_CAP else: cls.max_retries = max_retries @@ -98,11 +98,11 @@ def agent(cls, version=None): if not reuse_cached_agent: uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, - True, cls.max_retries, cls.retry_delay) + True, cls.max_retries, cls.base_retry_delay) return uncached_agent if cls.cached_agent is None: cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, - True, cls.max_retries, cls.retry_delay) + True, cls.max_retries, cls.base_retry_delay) return cls.cached_agent From 7358a270e700943bac97b6a00d10662e222678c7 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 20 Jan 2020 16:29:29 +0530 Subject: [PATCH 08/23] nit value --- bin/qds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/qds.py b/bin/qds.py index 66ec4cee..6959024f 100755 --- a/bin/qds.py +++ b/bin/qds.py @@ -628,7 +628,7 @@ def main(): options.max_retries = 6 if options.base_retry_delay is None: - options.base_retry_delay = 30 + options.base_retry_delay = 10 if options.cloud_name is None: options.cloud_name = "AWS" From 2aba030b4200131b2167b677b912973909d327bf Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Wed, 22 Jan 2020 18:14:16 +0530 Subject: [PATCH 09/23] improved logic --- bin/qds.py | 4 ++-- qds_sdk/connection.py | 4 ++-- qds_sdk/qubole.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/qds.py b/bin/qds.py index 6959024f..8433d2c9 100755 --- a/bin/qds.py +++ b/bin/qds.py @@ -590,7 +590,7 @@ def main(): optparser.add_option("--max_retries", dest="max_retries", type=int, default=os.getenv('QDS_MAX_RETRIES'), - help="Number of re-attempts for an api-call in case of retryable exceptions. defaults to 6." + help="Number of re-attempts for an api-call in case of retryable exceptions. defaults to 5." ) optparser.add_option("-v", dest="verbose", action="store_true", @@ -625,7 +625,7 @@ def main(): options.poll_interval = 5 if options.max_retries is None: - options.max_retries = 6 + options.max_retries = 5 if options.base_retry_delay is None: options.base_retry_delay = 10 diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 3c9cbaed..0d705b89 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -53,7 +53,7 @@ def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries= self.session_with_retries = requests.Session() self.session_with_retries.mount('https://', MyAdapter(max_retries=3)) - def retry(ExceptionToCheck, tries=6, delay=10, backoff=2): + def retry(ExceptionToCheck, tries=5, delay=10, backoff=2): def deco_retry(f): @wraps(f) def f_retry(self, *args, **kwargs): @@ -61,7 +61,7 @@ def f_retry(self, *args, **kwargs): mtries, mdelay = self.max_retries, self.base_retry_delay else: mtries, mdelay = tries, delay - while mtries > 1: + while mtries >= 1: try: return f(self, *args, **kwargs) except ExceptionToCheck as e: diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index ddbafa6f..4f566eb5 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -21,7 +21,7 @@ class Qubole: """ MIN_POLL_INTERVAL = 1 - RETRIES_CAP = 6 + RETRIES_CAP = 5 MAX_RETRY_DELAY = 10 _auth = None From b69b4b3f67432dcb7ce004de9f7fbed22264f57e Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Fri, 24 Jan 2020 11:47:17 +0530 Subject: [PATCH 10/23] typo err in max_retries --- qds_sdk/qubole.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index 4f566eb5..c0eacd8c 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -39,7 +39,7 @@ class Qubole: @classmethod def configure(cls, api_token, api_url="https://api.qubole.com/api/", version="v1.2", - poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", base_retry_delay=10, max_retries=6): + poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", base_retry_delay=10, max_retries=5): """ Set parameters governing interaction with QDS Args: From ada1662442a1800b4e669f1d94ababa234d3db2b Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 12:17:59 +0530 Subject: [PATCH 11/23] added message for 429 err code --- qds_sdk/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 0d705b89..0bc75b01 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -193,7 +193,7 @@ def _handle_error(response): raise RetryWithDelay(response, "Data requested is unavailable. Retrying ...") elif code == 429: sys.stderr.write(response.text + "\n") - raise RetryWithDelay(response) + raise RetryWithDelay(response, "Too many requests. Retrying ...") elif 401 <= code < 500: sys.stderr.write(response.text + "\n") raise ClientError(response) From 15dd43c67cbc874b7b0228a5083d8b82b09a6f49 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 12:21:24 +0530 Subject: [PATCH 12/23] nit: value correction --- qds_sdk/connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 0bc75b01..6be03c43 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -35,7 +35,7 @@ def init_poolmanager(self, connections, maxsize,block=False): class Connection: - def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=6, base_retry_delay=10): + def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=5, base_retry_delay=10): self.auth = auth self.rest_url = rest_url self.skip_ssl_cert_check = skip_ssl_cert_check From d72dfd84adfafcc5d882549265afe1b5977b8354 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 14:40:19 +0530 Subject: [PATCH 13/23] test From efbc8a2f1b23fea2b32be1fe068e4eb257d9f6e2 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 15:14:04 +0530 Subject: [PATCH 14/23] changes added style checks --- bin/qds.py | 11 ++++++----- qds_sdk/connection.py | 8 ++++---- qds_sdk/qubole.py | 12 ++++++------ 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/bin/qds.py b/bin/qds.py index 8433d2c9..26692dbf 100755 --- a/bin/qds.py +++ b/bin/qds.py @@ -585,13 +585,14 @@ def main(): optparser.add_option("--base_retry_delay", dest="base_retry_delay", type=int, default=os.getenv('QDS_BASE_RETRY_DELAY'), - help="base sleep interval for exponential backoff in case of retryable exceptions. defaults to 10s." - ) + help="base sleep interval for exponential backoff in case of " + "retryable exceptions.Defaults to 10s.") + optparser.add_option("--max_retries", dest="max_retries", type=int, default=os.getenv('QDS_MAX_RETRIES'), - help="Number of re-attempts for an api-call in case of retryable exceptions. defaults to 5." - ) + help="Number of re-attempts for an api-call in case of " + " retryable exceptions. Defaults to 5.") optparser.add_option("-v", dest="verbose", action="store_true", default=False, @@ -644,7 +645,7 @@ def main(): poll_interval=options.poll_interval, skip_ssl_cert_check=options.skip_ssl_cert_check, cloud_name=options.cloud_name, - base_retry_delay=options.base_retry_delay, + base_retry_delay=options.base_retry_delay, max_retries=options.max_retries ) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 6be03c43..30674a44 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -58,9 +58,9 @@ def deco_retry(f): @wraps(f) def f_retry(self, *args, **kwargs): if hasattr(self, 'max_retries'): - mtries, mdelay = self.max_retries, self.base_retry_delay + mtries, mdelay = self.max_retries, self.base_retry_delay else: - mtries, mdelay = tries, delay + mtries, mdelay = tries, delay while mtries >= 1: try: return f(self, *args, **kwargs) @@ -160,8 +160,8 @@ def _handle_error(response): if 'X-Qubole-Trace-Id' in response.headers: now = datetime.now() - time = now.strftime('%Y-%m-%d %H:%M:%S') - format_list = [time,response.headers['X-Qubole-Trace-Id']] + time_now = now.strftime('%Y-%m-%d %H:%M:%S') + format_list = [time_now,response.headers['X-Qubole-Trace-Id']] sys.stderr.write("[{}] Request ID is: {}. Please share it with Qubole Support team for any assistance".format(*format_list) + "\n") if code == 400: diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index c0eacd8c..d4906c25 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -65,13 +65,13 @@ def configure(cls, api_token, cls.cached_agent = None if base_retry_delay > Qubole.MAX_RETRY_DELAY: log.warn("Sleep between successive retries cannot be greater than %s seconds." - " Setting it to %s seconds.\n" % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) + " Setting it to %s seconds.\n" % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) cls.base_retry_delay = Qubole.MAX_RETRY_DELAY else: cls.base_retry_delay = base_retry_delay if max_retries > Qubole.RETRIES_CAP: log.warn("Maximum retries cannot be greater than %s." - " Setting it to default - %s.\n" % (Qubole.RETRIES_CAP, Qubole.RETRIES_CAP)) + " Setting it to default - %s.\n" % (Qubole.RETRIES_CAP, Qubole.RETRIES_CAP)) cls.max_retries = Qubole.RETRIES_CAP else: cls.max_retries = max_retries @@ -97,11 +97,11 @@ def agent(cls, version=None): raise ConfigError("No API Token specified - please supply one via Qubole.configure()") if not reuse_cached_agent: - uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, - True, cls.max_retries, cls.base_retry_delay) - return uncached_agent + uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, + True, cls.max_retries, cls.base_retry_delay) + return uncached_agent if cls.cached_agent is None: - cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, + cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, True, cls.max_retries, cls.base_retry_delay) return cls.cached_agent From 7872e98a245e6055e01181cec08ae71cc7caa115 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 15:30:40 +0530 Subject: [PATCH 15/23] indentations and style_checks --- qds_sdk/connection.py | 6 ++++-- qds_sdk/qubole.py | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 30674a44..eba0c891 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -35,7 +35,9 @@ def init_poolmanager(self, connections, maxsize,block=False): class Connection: - def __init__(self, auth, rest_url, skip_ssl_cert_check, reuse=True, max_retries=5, base_retry_delay=10): + def __init__(self, auth, rest_url, skip_ssl_cert_check, + reuse=True, max_retries=5, + base_retry_delay=10): self.auth = auth self.rest_url = rest_url self.skip_ssl_cert_check = skip_ssl_cert_check @@ -161,7 +163,7 @@ def _handle_error(response): if 'X-Qubole-Trace-Id' in response.headers: now = datetime.now() time_now = now.strftime('%Y-%m-%d %H:%M:%S') - format_list = [time_now,response.headers['X-Qubole-Trace-Id']] + format_list = [time_now, response.headers['X-Qubole-Trace-Id']] sys.stderr.write("[{}] Request ID is: {}. Please share it with Qubole Support team for any assistance".format(*format_list) + "\n") if code == 400: diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index d4906c25..e9870d61 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -39,7 +39,8 @@ class Qubole: @classmethod def configure(cls, api_token, api_url="https://api.qubole.com/api/", version="v1.2", - poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", base_retry_delay=10, max_retries=5): + poll_interval=5, skip_ssl_cert_check=False, cloud_name="AWS", + base_retry_delay=10, max_retries=5): """ Set parameters governing interaction with QDS Args: @@ -48,7 +49,8 @@ def configure(cls, api_token, `version`: QDS REST api version. Will be used throughout unless overridden in Qubole.agent(..) `poll_interval`: interval in secs when polling QDS for events `delay` : interval in secs to sleep in between successive retries - `retries` : maximum number of time to retry an api call in case of retryable exception. + `retries` : maximum number of time to retry an api call in case + of retryable exception. """ cls._auth = QuboleAuth(api_token) @@ -102,7 +104,7 @@ def agent(cls, version=None): return uncached_agent if cls.cached_agent is None: cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, - True, cls.max_retries, cls.base_retry_delay) + True, cls.max_retries, cls.base_retry_delay) return cls.cached_agent From c22507d2bbe37dd2cef34d9cdae776ce3d85977a Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 15:35:12 +0530 Subject: [PATCH 16/23] fixing style checks --- qds_sdk/connection.py | 2 +- qds_sdk/qubole.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index eba0c891..22ffdc11 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -36,7 +36,7 @@ def init_poolmanager(self, connections, maxsize,block=False): class Connection: def __init__(self, auth, rest_url, skip_ssl_cert_check, - reuse=True, max_retries=5, + reuse=True, max_retries=5, base_retry_delay=10): self.auth = auth self.rest_url = rest_url diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index e9870d61..962dce79 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -49,7 +49,7 @@ def configure(cls, api_token, `version`: QDS REST api version. Will be used throughout unless overridden in Qubole.agent(..) `poll_interval`: interval in secs when polling QDS for events `delay` : interval in secs to sleep in between successive retries - `retries` : maximum number of time to retry an api call in case + `retries` : maximum number of time to retry an api call in case of retryable exception. """ @@ -90,11 +90,11 @@ def agent(cls, version=None): """ reuse_cached_agent = True if version: - log.debug("api version changed to %s" % version) - cls.rest_url = '/'.join([cls.baseurl.rstrip('/'), version]) - reuse_cached_agent = False + log.debug("api version changed to %s" % version) + cls.rest_url = '/'.join([cls.baseurl.rstrip('/'), version]) + reuse_cached_agent = False else: - cls.rest_url = '/'.join([cls.baseurl.rstrip('/'), cls.version]) + cls.rest_url = '/'.join([cls.baseurl.rstrip('/'), cls.version]) if cls.api_token is None: raise ConfigError("No API Token specified - please supply one via Qubole.configure()") From 248ab7e92d984663a80be253da55715c1841df91 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 15:45:55 +0530 Subject: [PATCH 17/23] style checks-3 --- qds_sdk/connection.py | 7 ++++--- qds_sdk/qubole.py | 12 ++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index 22ffdc11..eea6fe32 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -68,7 +68,8 @@ def f_retry(self, *args, **kwargs): return f(self, *args, **kwargs) except ExceptionToCheck as e: logger = logging.getLogger("retry") - msg = "%s, Retrying in %d seconds..." % (e.__class__.__name__, mdelay) + msg = "%s, Retrying in %d seconds..." % (e.__class__.__name__, + mdelay) logger.info(msg) time.sleep(mdelay) mtries -= 1 @@ -192,10 +193,10 @@ def _handle_error(response): raise RetryWithDelay(response) elif code == 449: sys.stderr.write(response.text + "\n") - raise RetryWithDelay(response, "Data requested is unavailable. Retrying ...") + raise RetryWithDelay(response, "Data requested is unavailable. Retrying...") elif code == 429: sys.stderr.write(response.text + "\n") - raise RetryWithDelay(response, "Too many requests. Retrying ...") + raise RetryWithDelay(response, "Too many requests. Retrying...") elif 401 <= code < 500: sys.stderr.write(response.text + "\n") raise ClientError(response) diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index 962dce79..4780c80b 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -66,14 +66,17 @@ def configure(cls, api_token, cls.cloud_name = cloud_name.lower() cls.cached_agent = None if base_retry_delay > Qubole.MAX_RETRY_DELAY: - log.warn("Sleep between successive retries cannot be greater than %s seconds." - " Setting it to %s seconds.\n" % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) + log.warn("Sleep between successive retries cannot be greater than" + " %s seconds." + " Setting it to" + " %s seconds.\n" % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) cls.base_retry_delay = Qubole.MAX_RETRY_DELAY else: cls.base_retry_delay = base_retry_delay if max_retries > Qubole.RETRIES_CAP: log.warn("Maximum retries cannot be greater than %s." - " Setting it to default - %s.\n" % (Qubole.RETRIES_CAP, Qubole.RETRIES_CAP)) + " Setting it to" + " default - %s.\n" % (Qubole.RETRIES_CAP, Qubole.RETRIES_CAP)) cls.max_retries = Qubole.RETRIES_CAP else: cls.max_retries = max_retries @@ -103,7 +106,8 @@ def agent(cls, version=None): True, cls.max_retries, cls.base_retry_delay) return uncached_agent if cls.cached_agent is None: - cls.cached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, + cls.cached_agent = Connection(cls._auth, cls.rest_url, + cls.skip_ssl_cert_check, True, cls.max_retries, cls.base_retry_delay) return cls.cached_agent From b429a996b82fa2d1c25a08a2ffea8cd465e0cea0 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 15:51:10 +0530 Subject: [PATCH 18/23] FLK solved --- qds_sdk/qubole.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index 4780c80b..74485d80 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -69,7 +69,8 @@ def configure(cls, api_token, log.warn("Sleep between successive retries cannot be greater than" " %s seconds." " Setting it to" - " %s seconds.\n" % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) + " %s seconds.\n" + % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) cls.base_retry_delay = Qubole.MAX_RETRY_DELAY else: cls.base_retry_delay = base_retry_delay @@ -102,7 +103,8 @@ def agent(cls, version=None): raise ConfigError("No API Token specified - please supply one via Qubole.configure()") if not reuse_cached_agent: - uncached_agent = Connection(cls._auth, cls.rest_url, cls.skip_ssl_cert_check, + uncached_agent = Connection(cls._auth, cls.rest_url, + cls.skip_ssl_cert_check, True, cls.max_retries, cls.base_retry_delay) return uncached_agent if cls.cached_agent is None: From c22c88818b483c7443c3c715b2c179563e28b411 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 27 Jan 2020 15:54:32 +0530 Subject: [PATCH 19/23] FLK solved-2 --- qds_sdk/qubole.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qds_sdk/qubole.py b/qds_sdk/qubole.py index 74485d80..df830a2c 100644 --- a/qds_sdk/qubole.py +++ b/qds_sdk/qubole.py @@ -69,7 +69,7 @@ def configure(cls, api_token, log.warn("Sleep between successive retries cannot be greater than" " %s seconds." " Setting it to" - " %s seconds.\n" + " %s seconds.\n" % (Qubole.MAX_RETRY_DELAY, Qubole.MAX_RETRY_DELAY)) cls.base_retry_delay = Qubole.MAX_RETRY_DELAY else: From 77fac19d7edc5499ba854e715c770e633a318aa8 Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Wed, 29 Jan 2020 17:20:55 +0530 Subject: [PATCH 20/23] adding connection_test --- tests/test_connection.py | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/test_connection.py diff --git a/tests/test_connection.py b/tests/test_connection.py new file mode 100644 index 00000000..ae7486a1 --- /dev/null +++ b/tests/test_connection.py @@ -0,0 +1,48 @@ +import sys +import os + +if sys.version_info > (2, 7, 0): + import unittest +else: + import unittest2 as unittest +from mock import * + +sys.path.append(os.path.join(os.path.dirname(__file__), '../bin')) +import qds +from mock import Mock, ANY +from qds_sdk.connection import Connection +from test_base import print_command +from test_base import QdsCliTestCase + + +class TestConnection(QdsCliTestCase): + + #Test with correct values set + def test_connection_object(self): + sys.argv = ['qds.py', '--max_retries', '3', '--base_retry_delay', '2', 'cluster', 'list'] + print_command() + Connection.__init__ = Mock(return_value=None) + Connection._api_call = Mock(return_value={}) + qds.main() + Connection.__init__.assert_called_with(ANY, ANY, ANY, ANY, 3, 2) + + #Test with incorrect values + def test_connection_override(self): + sys.argv = ['qds.py', '--max_retries', '15', '--base_retry_delay', '15', 'cluster', 'list'] + print_command() + Connection.__init__ = Mock(return_value=None) + Connection._api_call = Mock(return_value={}) + qds.main() + Connection.__init__.assert_called_with(ANY, ANY, ANY, ANY, 5, 10) + + #Test with no values given should set default + def test_connection_default(self): + sys.argv = ['qds.py', 'cluster', 'list'] + print_command() + Connection.__init__ = Mock(return_value=None) + Connection._api_call = Mock(return_value={}) + qds.main() + Connection.__init__.assert_called_with(ANY, ANY, ANY, ANY, 5, 10) + +if __name__ == '__main__': + unittest.main() From 6cd65443db70e3cc901fbebd378bb3962911437a Mon Sep 17 00:00:00 2001 From: Saurabh Shekher Date: Mon, 3 Feb 2020 15:01:55 +0530 Subject: [PATCH 21/23] making new excepttion class and restoring old values --- qds_sdk/connection.py | 12 ++++++------ qds_sdk/exception.py | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/qds_sdk/connection.py b/qds_sdk/connection.py index eea6fe32..3ec4c0a8 100644 --- a/qds_sdk/connection.py +++ b/qds_sdk/connection.py @@ -78,23 +78,23 @@ def f_retry(self, *args, **kwargs): return f_retry # true decorator return deco_retry - @retry((RetryWithDelay, requests.Timeout)) + @retry((RetryWithDelay, requests.Timeout, ServerError, ApiThrottledRetry)) def get_raw(self, path, params=None): return self._api_call_raw("GET", path, params=params) - @retry((RetryWithDelay, requests.Timeout, ServerError)) + @retry((RetryWithDelay, requests.Timeout, ServerError, ApiThrottledRetry)) def get(self, path, params=None): return self._api_call("GET", path, params=params) - @retry((RetryWithDelay, requests.Timeout)) + @retry(ApiThrottledRetry) def put(self, path, data=None): return self._api_call("PUT", path, data) - @retry((RetryWithDelay, requests.Timeout)) + @retry(ApiThrottledRetry) def post(self, path, data=None): return self._api_call("POST", path, data) - @retry((RetryWithDelay, requests.Timeout)) + @retry(ApiThrottledRetry) def delete(self, path, data=None): return self._api_call("DELETE", path, data) @@ -196,7 +196,7 @@ def _handle_error(response): raise RetryWithDelay(response, "Data requested is unavailable. Retrying...") elif code == 429: sys.stderr.write(response.text + "\n") - raise RetryWithDelay(response, "Too many requests. Retrying...") + raise ApiThrottledRetry(response, "Too many requests. Retrying...") elif 401 <= code < 500: sys.stderr.write(response.text + "\n") raise ClientError(response) diff --git a/qds_sdk/exception.py b/qds_sdk/exception.py index ef06a335..b969a879 100644 --- a/qds_sdk/exception.py +++ b/qds_sdk/exception.py @@ -85,3 +85,7 @@ class MethodNotAllowed(ClientError): """An error raised when a method is not allowed.""" # 405 Method Not Allowed pass +class ApiThrottledRetry(ClientError): + """An error raised for 429 response is received.""" + # 429 Too Many Requests + pass From 4492c6573ae5a1135bc47f99dc513253a6db2bad Mon Sep 17 00:00:00 2001 From: shekharsaurabh <37484772+shekharsaurabh@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:29:48 +0530 Subject: [PATCH 22/23] update exception.py enhanced readability --- qds_sdk/exception.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qds_sdk/exception.py b/qds_sdk/exception.py index b969a879..3cacb12f 100644 --- a/qds_sdk/exception.py +++ b/qds_sdk/exception.py @@ -85,7 +85,8 @@ class MethodNotAllowed(ClientError): """An error raised when a method is not allowed.""" # 405 Method Not Allowed pass + class ApiThrottledRetry(ClientError): - """An error raised for 429 response is received.""" + """An error raised when upstream requests are throttled.""" # 429 Too Many Requests pass From 0c7214bf584af8341a1bec9fd39a06ede38c1bdf Mon Sep 17 00:00:00 2001 From: shekharsaurabh <37484772+shekharsaurabh@users.noreply.github.com> Date: Mon, 3 Feb 2020 15:45:30 +0530 Subject: [PATCH 23/23] flk error addressed --- qds_sdk/exception.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qds_sdk/exception.py b/qds_sdk/exception.py index 3cacb12f..5f545a30 100644 --- a/qds_sdk/exception.py +++ b/qds_sdk/exception.py @@ -86,6 +86,7 @@ class MethodNotAllowed(ClientError): # 405 Method Not Allowed pass + class ApiThrottledRetry(ClientError): """An error raised when upstream requests are throttled.""" # 429 Too Many Requests