diff options
author | Eelco Dolstra <edolstra@gmail.com> | 2019-06-24T19·48+0200 |
---|---|---|
committer | Eelco Dolstra <edolstra@gmail.com> | 2019-06-24T19·59+0200 |
commit | 78fa47a7f08a4cb6ee7061bf0bd86a40e1d6dc91 (patch) | |
tree | 96a4b4079640ca3435a929fa163bb07e921c3ecb /src/libutil | |
parent | 2fef4dd29673ef383b695480c9b8212f2e8d4711 (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')
-rw-r--r-- | src/libutil/retry.hh | 38 | ||||
-rw-r--r-- | src/libutil/types.hh | 2 |
2 files changed, 40 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)); + } + } +} + +} diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 92bf469b5c6f..88e3243f47a5 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -109,6 +109,8 @@ public: const string & msg() const { return err; } const string & prefix() const { return prefix_; } BaseError & addPrefix(const FormatOrString & fs); + + virtual bool isTransient() { return false; } }; #define MakeError(newClass, superClass) \ |