]> git.scottworley.com Git - git-cache/commitdiff
Retry fetch with backoff
authorScott Worley <scottworley@scottworley.com>
Sat, 18 Jul 2020 00:29:18 +0000 (17:29 -0700)
committerScott Worley <scottworley@scottworley.com>
Sat, 18 Jul 2020 00:31:06 +0000 (17:31 -0700)
Changelog
default.nix
git_cache.py
test.sh
test_git_cache.py

index 44b04c1984adb0cc596f18eee1810c5adbab4c34..b5ae99ce2a859121af9744329e94baff5f67b7cc 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -1,4 +1,6 @@
 ## [Unreleased]
 ## [Unreleased]
+### Added
+- Retry fetch with backoff
 
 
 ## [1.1.0] - 2020-07-16
 
 
 ## [1.1.0] - 2020-07-16
index 6c57b0d389bd9f9d1ae2c26ee2232b0ec5851078..6ac06fa4b6ee835eac286d273919fffc75493c83 100644 (file)
@@ -1,11 +1,34 @@
 { pkgs ? import <nixpkgs> { }, lint ? false }:
 { pkgs ? import <nixpkgs> { }, lint ? false }:
-pkgs.python3Packages.callPackage
-({ lib, buildPythonPackage, nix, git, autopep8, mypy, pylint, }:
+
+let
+  # Remove this package definition after https://github.com/NixOS/nixpkgs/pull/93377 reaches stable
+  backoff-fallback = { lib, buildPythonPackage, fetchPypi }:
+    buildPythonPackage rec {
+      pname = "backoff";
+      version = "1.10.0";
+      src = fetchPypi {
+        inherit pname version;
+        sha256 = "190rdpfhpjvb6bjh99fhdkgfsfkjwky7nz7b0nn5ah67z8hs1yxq";
+      };
+      meta = with lib; {
+        description = "Function decoration for backoff and retry";
+        homepage = "https://github.com/litl/backoff";
+        license = licenses.mit;
+        maintainers = with maintainers; [ chkno ];
+      };
+    };
+
+in pkgs.python3Packages.callPackage
+({ lib, buildPythonPackage, nix, git, backoff, autopep8, mypy, pylint, }:
   buildPythonPackage rec {
     pname = "git-cache";
     version = "1.2.0-pre";
     src = lib.cleanSource ./.;
   buildPythonPackage rec {
     pname = "git-cache";
     version = "1.2.0-pre";
     src = lib.cleanSource ./.;
+    propagatedBuildInputs = [ backoff ];
     checkInputs = [ nix git mypy ] ++ lib.optionals lint [ autopep8 pylint ];
     doCheck = true;
     checkPhase = "./test.sh";
     checkInputs = [ nix git mypy ] ++ lib.optionals lint [ autopep8 pylint ];
     doCheck = true;
     checkPhase = "./test.sh";
-  }) { }
+  }) {
+    backoff = pkgs.python3Packages.backoff or (pkgs.python3Packages.callPackage
+      backoff-fallback { });
+  }
index 594fb8a77cdad825a2783723ffc04bbd6c951b71..c848161076caa44f8bccfa7c28180c2da8ac13d8 100644 (file)
@@ -12,6 +12,8 @@ import sys
 
 from typing import Tuple
 
 
 from typing import Tuple
 
+import backoff
+
 Path = str  # eg: "/home/user/.cache/git-cache/v1"
 Repo = str  # eg: "https://github.com/NixOS/nixpkgs.git"
 Ref = str   # eg: "master" or "v1.0.0"
 Path = str  # eg: "/home/user/.cache/git-cache/v1"
 Repo = str  # eg: "https://github.com/NixOS/nixpkgs.git"
 Ref = str   # eg: "master" or "v1.0.0"
@@ -36,6 +38,17 @@ def verify_ancestry(repo: Repo, ref: Ref, rev: Rev) -> None:
                     rev, ref], check=True)
 
 
                     rev, ref], check=True)
 
 
+@backoff.on_exception(
+    backoff.expo,
+    subprocess.CalledProcessError,
+    max_time=lambda: int(os.environ.get('BACKOFF_MAX_TIME', '30')))
+def _git_fetch(cachedir: Path, repo: Repo, ref: Ref) -> None:
+    # We don't use --force here because we want to abort and freak out if forced
+    # updates are happening.
+    subprocess.run(['git', '-C', cachedir, 'fetch', repo,
+                    '%s:%s' % (ref, ref)], check=True)
+
+
 def fetch(repo: Repo, ref: Ref) -> Tuple[Path, Rev]:
     cachedir = git_cachedir(repo)
     if not os.path.exists(cachedir):
 def fetch(repo: Repo, ref: Ref) -> Tuple[Path, Rev]:
     cachedir = git_cachedir(repo)
     if not os.path.exists(cachedir):
@@ -43,10 +56,7 @@ def fetch(repo: Repo, ref: Ref) -> Tuple[Path, Rev]:
         subprocess.run(['git', 'init', '--bare', cachedir], check=True)
 
     logging.debug('Fetching ref "%s" from %s', ref, repo)
         subprocess.run(['git', 'init', '--bare', cachedir], check=True)
 
     logging.debug('Fetching ref "%s" from %s', ref, repo)
-    # We don't use --force here because we want to abort and freak out if forced
-    # updates are happening.
-    subprocess.run(['git', '-C', cachedir, 'fetch', repo,
-                    '%s:%s' % (ref, ref)], check=True)
+    _git_fetch(cachedir, repo, ref)
 
     with open(os.path.join(cachedir, 'refs', 'heads', ref)) as rev_file:
         rev = Rev(rev_file.read(999).strip())
 
     with open(os.path.join(cachedir, 'refs', 'heads', ref)) as rev_file:
         rev = Rev(rev_file.read(999).strip())
diff --git a/test.sh b/test.sh
index a0e3f5b309987d5ec3aff826df7099b63e56a1d5..dbdf643578b04f3125105cd71e6c29477a908a51 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -5,7 +5,7 @@ set -e
 PARALLELISM=4
 
 find . -name build -prune -o -name dist -prune -o -name '*.py' -print0 |
 PARALLELISM=4
 
 find . -name build -prune -o -name dist -prune -o -name '*.py' -print0 |
-  xargs -0 mypy --strict --ignore-missing-imports --no-warn-unused-ignores
+  xargs -0 mypy --strict --ignore-missing-imports --no-warn-unused-ignores --allow-untyped-decorators
 
 python3 -m unittest
 
 
 python3 -m unittest
 
index 7408cad13e340992c21f95b612f971bc77dbb0c0..ecb11e8e19c9bfd7a1c3ddbab27dd2d5568bdc83 100644 (file)
@@ -37,6 +37,8 @@ class TestGitCache(unittest.TestCase):
         os.environ['GIT_AUTHOR_EMAIL'] = 'test_git_cache@example.com'
         os.environ['GIT_COMMITTER_EMAIL'] = 'test_git_cache@example.com'
 
         os.environ['GIT_AUTHOR_EMAIL'] = 'test_git_cache@example.com'
         os.environ['GIT_COMMITTER_EMAIL'] = 'test_git_cache@example.com'
 
+        os.environ['BACKOFF_MAX_TIME'] = '0'
+
         self.tempdir = tempfile.TemporaryDirectory(prefix='git_cache_test-')
         self.upstream = os.path.join(self.tempdir.name, 'upstream')
         subprocess.run(['git', 'init', self.upstream], check=True)
         self.tempdir = tempfile.TemporaryDirectory(prefix='git_cache_test-')
         self.upstream = os.path.join(self.tempdir.name, 'upstream')
         subprocess.run(['git', 'init', self.upstream], check=True)