# The 'requests' library enables urllib3's SNI support by default, which uses pyopenssl.
# However, urllib3 prior to version 1.10 has a major bug in this feature
# (OpenSSL WantWriteError, https://github.com/shazow/urllib3/issues/412)
- # Unfortunately a certain major Linux distribution is stablizing on urllib3
- # 1.9.1 which means the following workaround is necessary to be able to use
+ # Unfortunately Debian 8 is stabilizing on urllib3 1.9.1 which means the
+ # following workaround is necessary to be able to use
# the arvados python sdk with the distribution-provided packages.
-
import urllib3
- urllib3_ok = False
- urllib3version = re.match(r'(\d+)\.(\d+)\.(\d+)', urllib3.__version__)
- if (urllib3version and
- int(urllib3version.group(1)) == 1 and
- int(urllib3version.group(2)) >= 10):
- urllib3_ok = True
- if not urllib3_ok:
+ from pkg_resources import parse_version
+ if parse_version(urllib3.__version__) < parse_version('1.10'):
from urllib3.contrib import pyopenssl
pyopenssl.extract_from_urllib3()
except ImportError:
KeepClient does not use a proxy, pass in an empty string.
:timeout:
- The timeout (in seconds) for HTTP requests to Keep
+ The initial timeout (in seconds) for HTTP requests to Keep
non-proxy servers. A tuple of two floats is interpreted as
(connection_timeout, read_timeout): see
http://docs.python-requests.org/en/latest/user/advanced/#timeouts.
+ Because timeouts are often a result of transient server load, the
+ actual connection timeout will be increased by a factor of two on
+ each retry.
Default: (2, 300).
:proxy_timeout:
- The timeout (in seconds) for HTTP requests to
+ The initial timeout (in seconds) for HTTP requests to
Keep proxies. A tuple of two floats is interpreted as
- (connection_timeout, read_timeout). Default: (20, 300).
+ (connection_timeout, read_timeout). The behavior described
+ above for adjusting connection timeouts on retry also applies.
+ Default: (20, 300).
:api_token:
If you're not using an API client, but only talking
self.using_proxy = None
self._static_services_list = False
- def current_timeout(self):
- """Return the appropriate timeout to use for this client: the proxy
- timeout setting if the backend service is currently a proxy,
- the regular timeout setting otherwise.
+ def current_timeout(self, attempt_number):
+ """Return the appropriate timeout to use for this client.
+
+ The proxy timeout setting if the backend service is currently a proxy,
+ the regular timeout setting otherwise. The `attempt_number` indicates
+ how many times the operation has been tried already (starting from 0
+ for the first try), and scales the connection timeout portion of the
+ return value accordingly.
+
"""
# TODO(twp): the timeout should be a property of a
# KeepService, not a KeepClient. See #4488.
- return self.proxy_timeout if self.using_proxy else self.timeout
+ t = self.proxy_timeout if self.using_proxy else self.timeout
+ return (t[0] * (1 << attempt_number), t[1])
def build_services_list(self, force_rebuild=False):
if (self._static_services_list or
for root in (local_roots + hint_roots)
if roots_map[root].usable()]
for keep_service in services_to_try:
- blob = keep_service.get(locator, timeout=self.current_timeout())
+ blob = keep_service.get(locator, timeout=self.current_timeout(num_retries-tries_left))
if blob is not None:
break
loop.save_result((blob, len(services_to_try)))
data_hash=data_hash,
service_root=service_root,
thread_limiter=thread_limiter,
- timeout=self.current_timeout())
+ timeout=self.current_timeout(num_retries-tries_left))
t.start()
threads.append(t)
for t in threads: