about summary refs log tree commit diff
path: root/src/libutil/retry.hh
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-06-24T19·48+0200
committerEelco Dolstra <edolstra@gmail.com>2019-06-24T19·59+0200
commit78fa47a7f08a4cb6ee7061bf0bd86a40e1d6dc91 (patch)
tree96a4b4079640ca3435a929fa163bb07e921c3ecb /src/libutil/retry.hh
parent2fef4dd29673ef383b695480c9b8212f2e8d4711 (diff)
Fix 'error 9 while decompressing xz file'
Once we've started writing data to a Sink, we can't restart a download
request, because then we end up writing duplicate data to the
Sink. Therefore we shouldn't handle retries in Downloader but at a
higher level (in particular, in copyStorePath()).

Fixes #2952.

(cherry picked from commit a67cf5a3585c41dd9f219a2c7aa9cf67fa69520b)
Diffstat (limited to 'src/libutil/retry.hh')
-rw-r--r--src/libutil/retry.hh38
1 files changed, 38 insertions, 0 deletions
diff --git a/src/libutil/retry.hh b/src/libutil/retry.hh
new file mode 100644
index 000000000000..b45cb37f736b
--- /dev/null
+++ b/src/libutil/retry.hh
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "logging.hh"
+
+#include <functional>
+#include <cmath>
+#include <random>
+#include <thread>
+
+namespace nix {
+
+inline unsigned int retrySleepTime(unsigned int attempt)
+{
+    std::random_device rd;
+    std::mt19937 mt19937;
+    return 250.0 * std::pow(2.0f,
+        attempt - 1 + std::uniform_real_distribution<>(0.0, 0.5)(mt19937));
+}
+
+template<typename C>
+C retry(unsigned int attempts, std::function<C()> && f)
+{
+    unsigned int attempt = 0;
+    while (true) {
+        try {
+            return f();
+        } catch (BaseError & e) {
+            ++attempt;
+            if (attempt >= attempts || !e.isTransient())
+                throw;
+            auto ms = retrySleepTime(attempt);
+            warn("%s; retrying in %d ms", e.what(), ms);
+            std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+        }
+    }
+}
+
+}