phabricator: introduce a `phabricator.retry` option
authorPierre-Yves David <pierre-yves.david@octobus.net>
Sat, 28 Nov 2020 19:58:37 +0100
changeset 45969 57183111a463
parent 45968 971424517e17
child 45970 4d70444c3ea9
phabricator: introduce a `phabricator.retry` option For the past 2 days, my connection to phab.mercurial-scm.org became extremely poor. In practice this mean that any conduit call has a fairly high change to hang and die. Giving the amount of call done by the phabricator extension, it means the when I am lucky I can get 1 or 2 Diff to update after a few try, but anything sizeable doesn't have any hope to get through. This changesets introduce a new option for the fabricator extension to try retry failed command itself. So that I can get Diff through. As you can guess, this changeset managed to reach Phabricator thanks to itself. Differential Revision: https://phab.mercurial-scm.org/D9449
hgext/phabricator.py
--- a/hgext/phabricator.py	Tue Nov 24 16:17:16 2020 -0500
+++ b/hgext/phabricator.py	Sat Nov 28 19:58:37 2020 +0100
@@ -35,6 +35,14 @@
     # the internal library.
     curlcmd = curl --connect-timeout 2 --retry 3 --silent
 
+    # retry failed command N time (default 0). Useful when using the extension
+    # over flakly connection.
+    #
+    # We wait `retry.interval` between each retry, in seconds.
+    # (default 1 second).
+    retry = 3
+    retry.interval = 10
+
     [auth]
     example.schemes = https
     example.prefix = phab.example.com
@@ -53,6 +61,7 @@
 import mimetypes
 import operator
 import re
+import time
 
 from mercurial.node import bin, nullid, short
 from mercurial.i18n import _
@@ -136,6 +145,16 @@
 )
 eh.configitem(
     b'phabricator',
+    b'retry',
+    default=0,
+)
+eh.configitem(
+    b'phabricator',
+    b'retry.interval',
+    default=1,
+)
+eh.configitem(
+    b'phabricator',
     b'url',
     default=None,
 )
@@ -400,8 +419,22 @@
     else:
         urlopener = urlmod.opener(ui, authinfo)
         request = util.urlreq.request(pycompat.strurl(url), data=data)
-        with contextlib.closing(urlopener.open(request)) as rsp:
-            body = rsp.read()
+        max_try = ui.configint(b'phabricator', b'retry') + 1
+        for try_count in range(max_try):
+            try:
+                with contextlib.closing(urlopener.open(request)) as rsp:
+                    body = rsp.read()
+                break
+            except util.urlerr.urlerror as err:
+                if try_count == max_try - 1:
+                    raise
+                ui.debug(
+                    b'Conduit Request failed (try %d/%d): %r\n'
+                    % (try_count + 1, max_try, err)
+                )
+                # failing request might come from overloaded server
+                retry_interval = ui.configint(b'phabricator', b'retry.interval')
+                time.sleep(retry_interval)
     ui.debug(b'Conduit Response: %s\n' % body)
     parsed = pycompat.rapply(
         lambda x: encoding.unitolocal(x)