123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- Retry requests on server error
- ******************************
- If lazr.restfulclient talks to a server that sends out a server-side
- error with status codes 502 or 503, the client will wait a few seconds
- and try the request again. Eventually it will give up and escalate the
- error code in the form of an exception.
- To test this, let's simulate a lazr.restful server prone to transient
- errors using a WSGI application.
- >>> import pkg_resources
- >>> wadl_string = pkg_resources.resource_string(
- ... 'wadllib.tests.data', 'launchpad-wadl.xml')
- >>> representations = { 'application/vnd.sun.wadl+xml' : wadl_string,
- ... 'application/json' : '{}' }
- This application will cause one request to fail for every item in its
- BROKEN_RESPONSES list.
- >>> BROKEN_RESPONSES = []
- >>> def broken_application(environ, start_response):
- ... if len(BROKEN_RESPONSES) > 0:
- ... start_response(str(BROKEN_RESPONSES.pop()),
- ... [('Content-type', 'text/plain')])
- ... return ["Sorry, I'm still broken."]
- ... else:
- ... media_type = environ['HTTP_ACCEPT']
- ... content = representations[media_type]
- ... start_response(
- ... '200', [('Content-type', media_type)])
- ... return [content]
- >>> def make_broken_application():
- ... return broken_application
- >>> import wsgi_intercept
- >>> wsgi_intercept.add_wsgi_intercept(
- ... 'api.launchpad.dev', 80, make_broken_application)
- >>> BROKEN_RESPONSES = []
- >>> from wsgi_intercept.httplib2_intercept import install
- >>> install()
- Here's a fake implementation of time.sleep() so that this test doesn't
- take a really long time to run, and so we can visualize sleep() being
- called as lazr.restfulclient retries over and over again.
- >>> def fake_sleep(time):
- ... print "sleep(%s) called" % time
- >>> import lazr.restfulclient._browser
- >>> old_sleep = lazr.restfulclient._browser.sleep
- >>> lazr.restfulclient._browser.sleep = fake_sleep
- As it starts out, the application isn't broken at all.
- >>> from lazr.restfulclient.resource import ServiceRoot
- >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
- Let's queue up one broken response. The client will sleep once and
- try again.
- >>> BROKEN_RESPONSES = [502]
- >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
- sleep(0) called
- Now the application will fail six times and then start working.
- >>> BROKEN_RESPONSES = [502, 503, 502, 503, 502, 503]
- >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
- sleep(0) called
- sleep(1) called
- sleep(2) called
- sleep(4) called
- sleep(8) called
- sleep(16) called
- Now the application will fail seven times and then start working. But
- the client will give up before then--it will only retry the request
- six times.
- >>> BROKEN_RESPONSES = [502, 503, 502, 503, 502, 503, 502]
- >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
- Traceback (most recent call last):
- ...
- ServerError: HTTP Error 502:
- ...
- By increasing the 'max_retries' constructor argument, we can make the
- application try more than six times, and eventually succeed.
- >>> BROKEN_RESPONSES = [502, 503, 502, 503, 502, 503, 502]
- >>> client = ServiceRoot(None, "http://api.launchpad.dev/",
- ... max_retries=10)
- sleep(0) called
- sleep(1) called
- sleep(2) called
- sleep(4) called
- sleep(8) called
- sleep(16) called
- sleep(32) called
- Now the application will fail once and then give a 400 error. The
- client will not retry in hopes that the 400 error will go away--400 is
- a client error.
- >>> BROKEN_RESPONSES = [502, 400]
- >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
- Traceback (most recent call last):
- ...
- BadRequest: HTTP Error 400:
- ...
- Teardown.
- >>> _ = wsgi_intercept.remove_wsgi_intercept("api.launchpad.dev", 80)
- >>> lazr.restfulclient._browser.sleep = old_sleep
|