I'm using zeep. The service I am using generates timeout errors every now and then, and I want to use automatic retry functionality.
I am trying to use a Requests retry session, but the timeout exception is not being caught and no retry is attempted.
I set up a Requests retry session (below) and I set up the client attribute of my class (currently with a timeout value to cause errors):
session = requests_retry_session()
transport = Transport(session=session,timeout=20,operation_timeout=.001)
self.client = Client(self.wsdl_url,transport=transport)
...
def requests_retry_session(
retries=10,
backoff_factor=0.3,
status_forcelist=(500, 502, 503, 504),
session=None,
) -> requests.Session:
session = session or requests.Session()
retry = Retry(
total=retries,
read=retries,
connect=retries,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
I'm getting a requests.exceptions.ReadTimeout (I assume this error is the same as the one I'm getting in real life, but with a 20s timeout, my logging is not good enough yet to be sure)
The exception is not being handled by the session.
Why not?
EDIT: zeep sends a POST request somewhere, which I did not find with my own debugging but activating debug logging in Requests shows it.
Retry does not work on POST by default.
https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html
I set method_whitelist=False to change that, and now I am triggering retries.
I vary this parameter based on the API I'm working with, but I usually set it to lower than 10, usually 3 retries is enough. The HTTP response codes to retry on.
Add a retry strategy to your HTTP client is straightforward. We create a HTTPAdapter and pass our strategy to the adapter. The default Retry class offers sane defaults, but is highly configurable so here is a rundown of the most common parameters I use. The parameters below include the default parameters the requests library uses.
retry with all HTTP methods ( None = a falsy value = retry on any verb, you can also use a list of uppercase HTTP verbs if desired to limit the retries to specific verbs) since a 503 was returned, first retry was sent after 0 seconds (immediate retry) Thus it took a total of 2+4+8 = 14 seconds (plus latencies) to raise a requests.RetryError.
It's done on a per-request basis. httpbin makes this easy to test. With a sleep delay of 10 seconds it will never work (with a timeout of 5 seconds) but it does use the timeout this time. Same code as above but with a 5 second timeout: That makes sense. Same backoff algorithm as before but now with 5 seconds for each attempt:
set method_whitelist=False
which will trigger retries when POSTs fail. Be careful; you need to know if repeated POSTs to your endpoint are a bad idea.
Here is some code setting up a retry session with requests:
from zeep import Client
from zeep.transports import Transport
import requests
from requests.adapters import HTTPAdapter, Retry
def requests_retry_session(
retries=5,
backoff_factor=0.3,
status_forcelist=(500, 502, 503, 504),
session=None,**kwargs) -> requests.Session:
session = session or requests.Session()
retry = Retry(
total=retries,
read=retries,
connect=retries,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
**kwargs
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
And then to make an instance of the Zeep client which will retry on POST fails
session = requests_retry_session(method_whitelist=False)
transport = Transport(session=session,timeout=30,operation_timeout=30)
zeep_client = Client(your_wsdl_url,transport=transport)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With