retry.standalone.rst 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. Retry requests on server error
  2. ******************************
  3. If lazr.restfulclient talks to a server that sends out a server-side
  4. error with status codes 502 or 503, the client will wait a few seconds
  5. and try the request again. Eventually it will give up and escalate the
  6. error code in the form of an exception.
  7. To test this, let's simulate a lazr.restful server prone to transient
  8. errors using a WSGI application.
  9. >>> import pkg_resources
  10. >>> wadl_string = pkg_resources.resource_string(
  11. ... 'wadllib.tests.data', 'launchpad-wadl.xml')
  12. >>> representations = { 'application/vnd.sun.wadl+xml' : wadl_string,
  13. ... 'application/json' : '{}' }
  14. This application will cause one request to fail for every item in its
  15. BROKEN_RESPONSES list.
  16. >>> BROKEN_RESPONSES = []
  17. >>> def broken_application(environ, start_response):
  18. ... if len(BROKEN_RESPONSES) > 0:
  19. ... start_response(str(BROKEN_RESPONSES.pop()),
  20. ... [('Content-type', 'text/plain')])
  21. ... return ["Sorry, I'm still broken."]
  22. ... else:
  23. ... media_type = environ['HTTP_ACCEPT']
  24. ... content = representations[media_type]
  25. ... start_response(
  26. ... '200', [('Content-type', media_type)])
  27. ... return [content]
  28. >>> def make_broken_application():
  29. ... return broken_application
  30. >>> import wsgi_intercept
  31. >>> wsgi_intercept.add_wsgi_intercept(
  32. ... 'api.launchpad.dev', 80, make_broken_application)
  33. >>> BROKEN_RESPONSES = []
  34. >>> from wsgi_intercept.httplib2_intercept import install
  35. >>> install()
  36. Here's a fake implementation of time.sleep() so that this test doesn't
  37. take a really long time to run, and so we can visualize sleep() being
  38. called as lazr.restfulclient retries over and over again.
  39. >>> def fake_sleep(time):
  40. ... print "sleep(%s) called" % time
  41. >>> import lazr.restfulclient._browser
  42. >>> old_sleep = lazr.restfulclient._browser.sleep
  43. >>> lazr.restfulclient._browser.sleep = fake_sleep
  44. As it starts out, the application isn't broken at all.
  45. >>> from lazr.restfulclient.resource import ServiceRoot
  46. >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
  47. Let's queue up one broken response. The client will sleep once and
  48. try again.
  49. >>> BROKEN_RESPONSES = [502]
  50. >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
  51. sleep(0) called
  52. Now the application will fail six times and then start working.
  53. >>> BROKEN_RESPONSES = [502, 503, 502, 503, 502, 503]
  54. >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
  55. sleep(0) called
  56. sleep(1) called
  57. sleep(2) called
  58. sleep(4) called
  59. sleep(8) called
  60. sleep(16) called
  61. Now the application will fail seven times and then start working. But
  62. the client will give up before then--it will only retry the request
  63. six times.
  64. >>> BROKEN_RESPONSES = [502, 503, 502, 503, 502, 503, 502]
  65. >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
  66. Traceback (most recent call last):
  67. ...
  68. ServerError: HTTP Error 502:
  69. ...
  70. By increasing the 'max_retries' constructor argument, we can make the
  71. application try more than six times, and eventually succeed.
  72. >>> BROKEN_RESPONSES = [502, 503, 502, 503, 502, 503, 502]
  73. >>> client = ServiceRoot(None, "http://api.launchpad.dev/",
  74. ... max_retries=10)
  75. sleep(0) called
  76. sleep(1) called
  77. sleep(2) called
  78. sleep(4) called
  79. sleep(8) called
  80. sleep(16) called
  81. sleep(32) called
  82. Now the application will fail once and then give a 400 error. The
  83. client will not retry in hopes that the 400 error will go away--400 is
  84. a client error.
  85. >>> BROKEN_RESPONSES = [502, 400]
  86. >>> client = ServiceRoot(None, "http://api.launchpad.dev/")
  87. Traceback (most recent call last):
  88. ...
  89. BadRequest: HTTP Error 400:
  90. ...
  91. Teardown.
  92. >>> _ = wsgi_intercept.remove_wsgi_intercept("api.launchpad.dev", 80)
  93. >>> lazr.restfulclient._browser.sleep = old_sleep