From 3f481929e09c3093c1fe0925fd8207ca3b567aa7 Mon Sep 17 00:00:00 2001 From: gzhytar Date: Sun, 12 Nov 2017 15:02:09 +0100 Subject: [PATCH 01/10] Implementing simple mechanism for retrying failed requests --- krakenex/api.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/krakenex/api.py b/krakenex/api.py index b5177b2..82eef07 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -27,6 +27,7 @@ import hashlib import hmac import base64 +import logging from . import version @@ -109,20 +110,34 @@ def _query(self, urlpath, data, headers=None): :raises: :py:exc:`requests.HTTPError`: if response status not successful """ + logger = logging.getLogger() + if data is None: data = {} if headers is None: headers = {} - + url = self.uri + urlpath - self.response = self.session.post(url, data = data, headers = headers) - - if self.response.status_code not in (200, 201, 202): - self.response.raise_for_status() - - return self.response.json() - + max_retries = 3 + attempt = 1 + + # Retries mechanism for certain HTTP codes. + # Kraken is behind CloudFlare which adds to network requests instability during peaks + # Careful! Sometimes service returns error code but actuallu executes a request + # needs investigation if this can cause a multiple buys/sells (don't think so as there is nonce in each request ) + while attempt<=max_retries: + self.response = self.session.post(url, data = data, headers = headers) + + if self.response.status_code in (200, 201, 202): + return self.response.json() + elif self.response.status_code in (504, 520) and attempt Date: Sun, 12 Nov 2017 15:37:27 +0100 Subject: [PATCH 02/10] Connection: Adding an attribute to configure a number of connection recovery attempts --- krakenex/api.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/krakenex/api.py b/krakenex/api.py index 82eef07..e0b8c0a 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -67,6 +67,8 @@ def __init__(self, key='', secret=''): 'User-Agent': 'krakenex/' + version.__version__ + ' (+' + version.__url__ + ')' }) self.response = None + # How many times we try to recover from bad HTTP connection situation + self.bad_http_connection_retries = 3 return def close(self): @@ -119,22 +121,19 @@ def _query(self, urlpath, data, headers=None): url = self.uri + urlpath - max_retries = 3 - attempt = 1 - # Retries mechanism for certain HTTP codes. # Kraken is behind CloudFlare which adds to network requests instability during peaks # Careful! Sometimes service returns error code but actuallu executes a request # needs investigation if this can cause a multiple buys/sells (don't think so as there is nonce in each request ) - while attempt<=max_retries: + attempt = 1 + while attempt<=self.bad_http_connection_retries: self.response = self.session.post(url, data = data, headers = headers) if self.response.status_code in (200, 201, 202): return self.response.json() - elif self.response.status_code in (504, 520) and attempt Date: Thu, 30 Nov 2017 20:36:51 +0200 Subject: [PATCH 03/10] api: rework _query() `retries` code with suggestions from PR #65. --- krakenex/api.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/krakenex/api.py b/krakenex/api.py index e0b8c0a..d01dd93 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -67,8 +67,13 @@ def __init__(self, key='', secret=''): 'User-Agent': 'krakenex/' + version.__version__ + ' (+' + version.__url__ + ')' }) self.response = None - # How many times we try to recover from bad HTTP connection situation - self.bad_http_connection_retries = 3 + + # retry-on-failure configuration + self.retries = 1 + self.cooldown = 15 + self.successcodes = [200, 201, 202] + self.retrycodes = [504, 520] + return def close(self): @@ -121,23 +126,24 @@ def _query(self, urlpath, data, headers=None): url = self.uri + urlpath - # Retries mechanism for certain HTTP codes. - # Kraken is behind CloudFlare which adds to network requests instability during peaks - # Careful! Sometimes service returns error code but actuallu executes a request - # needs investigation if this can cause a multiple buys/sells (don't think so as there is nonce in each request ) - attempt = 1 - while attempt<=self.bad_http_connection_retries: + attempt = 0 + while attempt < self.retries: + logger.debug('Posting query: nonce %d, attempt %d.', data['nonce'], attempt) self.response = self.session.post(url, data = data, headers = headers) + status = self.response.status_code - if self.response.status_code in (200, 201, 202): - return self.response.json() - elif self.response.status_code in (504, 520) and attempt Date: Thu, 30 Nov 2017 20:51:37 +0200 Subject: [PATCH 04/10] api: _query(): set nonce to -1 (not used) if query public. --- krakenex/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/krakenex/api.py b/krakenex/api.py index d01dd93..794823b 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -128,7 +128,8 @@ def _query(self, urlpath, data, headers=None): attempt = 0 while attempt < self.retries: - logger.debug('Posting query: nonce %d, attempt %d.', data['nonce'], attempt) + nonce = -1 if 'nonce' not in data.keys() else data['nonce'] # UGLY + logger.debug('Posting query: nonce %d, attempt %d.', nonce, attempt) self.response = self.session.post(url, data = data, headers = headers) status = self.response.status_code From de1c43d29ab05f0eab562b1b396f172cd0a74d17 Mon Sep 17 00:00:00 2001 From: Noel Maersk Date: Thu, 30 Nov 2017 20:53:20 +0200 Subject: [PATCH 05/10] api: add FIXME - getting logger for root ATM. --- krakenex/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/krakenex/api.py b/krakenex/api.py index 794823b..4014d85 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -117,6 +117,7 @@ def _query(self, urlpath, data, headers=None): :raises: :py:exc:`requests.HTTPError`: if response status not successful """ + # FIXME: have a logging object; reported as `root` otherwise logger = logging.getLogger() if data is None: From beb873f8c4aae58c04cfb276d5377f188b981f15 Mon Sep 17 00:00:00 2001 From: Noel Maersk Date: Fri, 1 Dec 2017 02:08:40 +0200 Subject: [PATCH 06/10] api: _query(): don't sleep if there's no retries to be made. --- krakenex/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/krakenex/api.py b/krakenex/api.py index 4014d85..cb342d5 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -69,7 +69,7 @@ def __init__(self, key='', secret=''): self.response = None # retry-on-failure configuration - self.retries = 1 + self.retries = 1 # FIXME: retries==1 does 0 retries! self.cooldown = 15 self.successcodes = [200, 201, 202] self.retrycodes = [504, 520] @@ -129,6 +129,7 @@ def _query(self, urlpath, data, headers=None): attempt = 0 while attempt < self.retries: + attempt += 1 nonce = -1 if 'nonce' not in data.keys() else data['nonce'] # UGLY logger.debug('Posting query: nonce %d, attempt %d.', nonce, attempt) self.response = self.session.post(url, data = data, headers = headers) @@ -140,7 +141,7 @@ def _query(self, urlpath, data, headers=None): logger.debug('HTTP error %d', status) logger.debug('Sleeping for %d seconds', self.cooldown) time.sleep(self.cooldown) - attempt += 1 + continue else: self.response.raise_for_status() From 13595368ce78623ce2d71d562bded5d94ce9546b Mon Sep 17 00:00:00 2001 From: Noel Maersk Date: Thu, 7 Dec 2017 22:55:34 +0200 Subject: [PATCH 07/10] api: retries=0 will attempt once. Thanks to /u/FBAThrow for reminding! https://www.reddit.com/r/krakenex/comments/778uvh/psa_http_error_502_does_not_mean_the_query_wont/dqwlc1a/ --- krakenex/api.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/krakenex/api.py b/krakenex/api.py index cb342d5..c07bf40 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -69,7 +69,7 @@ def __init__(self, key='', secret=''): self.response = None # retry-on-failure configuration - self.retries = 1 # FIXME: retries==1 does 0 retries! + self.retries = 0 self.cooldown = 15 self.successcodes = [200, 201, 202] self.retrycodes = [504, 520] @@ -127,17 +127,17 @@ def _query(self, urlpath, data, headers=None): url = self.uri + urlpath - attempt = 0 - while attempt < self.retries: - attempt += 1 + attempts = 0 + while attempts <= self.retries: nonce = -1 if 'nonce' not in data.keys() else data['nonce'] # UGLY - logger.debug('Posting query: nonce %d, attempt %d.', nonce, attempt) + logger.debug('Posting query: nonce %d, attempt %d.', nonce, attempts) self.response = self.session.post(url, data = data, headers = headers) status = self.response.status_code + attempts += 1 if status in self.successcodes: break - elif status in self.retrycodes and attempt < self.retries: + elif status in self.retrycodes and attempts <= self.retries: logger.debug('HTTP error %d', status) logger.debug('Sleeping for %d seconds', self.cooldown) time.sleep(self.cooldown) From b913d8d95e043af16b993d0ade5e5fc14a9666fc Mon Sep 17 00:00:00 2001 From: Noel Maersk Date: Thu, 30 Nov 2017 20:41:40 +0200 Subject: [PATCH 08/10] misc: add CHANGELOG stub. --- CHANGELOG.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e0cbbbe..b86b2d5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,15 @@ to `semantic versioning`_. .. _Keep a Changelog: http://keepachangelog.com/ .. _semantic versioning: http://semver.org/ +[vX.Y.Z] - TODO +--------------- + +Added +^^^^^ +* Allow configuring the ``krakenex.API`` object to retry the query + on certain HTTP error codes. By default, no retries will be attempted. +* TODO: docs on how, link here. + [v2.0.0] - 2017-11-14 (Tuesday) ------------------------------- From 7b93f3c2a33deeb3324e15532e43ea1d484f7eb5 Mon Sep 17 00:00:00 2001 From: Noel Maersk Date: Thu, 13 Sep 2018 17:15:27 +0300 Subject: [PATCH 09/10] api: minor, clean up whitespace. --- krakenex/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/krakenex/api.py b/krakenex/api.py index d62a867..6e3c9c4 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -140,7 +140,7 @@ def _query(self, urlpath, data, headers=None, timeout=None): data = {} if headers is None: headers = {} - + url = self.uri + urlpath attempts = 0 @@ -151,7 +151,7 @@ def _query(self, urlpath, data, headers=None, timeout=None): timeout=timeout) status = self.response.status_code attempts += 1 - + if status in self.successcodes: break elif status in self.retrycodes and attempts <= self.retries: From 9aabdafbabac1fabb4a613e27a0385112f22c527 Mon Sep 17 00:00:00 2001 From: Noel Maersk Date: Thu, 13 Sep 2018 17:24:58 +0300 Subject: [PATCH 10/10] api: give `logger` a name ('krakenex.api'). --- krakenex/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/krakenex/api.py b/krakenex/api.py index 6e3c9c4..279af08 100644 --- a/krakenex/api.py +++ b/krakenex/api.py @@ -133,8 +133,7 @@ def _query(self, urlpath, data, headers=None, timeout=None): :raises: :py:exc:`requests.HTTPError`: if response status not successful """ - # FIXME: have a logging object; reported as `root` otherwise - logger = logging.getLogger() + logger = logging.getLogger('krakenex.api') if data is None: data = {}