1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 14:51:16 +02:00

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 a67cf5a358)
This commit is contained in:
Eelco Dolstra 2019-06-24 21:48:52 +02:00
parent 2fef4dd296
commit 78fa47a7f0
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
7 changed files with 156 additions and 119 deletions

View file

@ -2,6 +2,7 @@
#include "download.hh"
#include "globals.hh"
#include "nar-info-disk-cache.hh"
#include "retry.hh"
namespace nix {
@ -114,7 +115,6 @@ protected:
DownloadRequest makeRequest(const std::string & path)
{
DownloadRequest request(cacheUri + "/" + path);
request.tries = 8;
return request;
}
@ -137,21 +137,46 @@ protected:
{
checkEnabled();
auto request(makeRequest(path));
struct State
{
DownloadRequest request;
std::function<void()> tryDownload;
unsigned int attempt = 0;
State(DownloadRequest && request) : request(request) {}
};
getDownloader()->enqueueDownload(request,
{[callback, this](std::future<DownloadResult> result) {
try {
callback(result.get().data);
} catch (DownloadError & e) {
if (e.error == Downloader::NotFound || e.error == Downloader::Forbidden)
return callback(std::shared_ptr<std::string>());
maybeDisable();
callback.rethrow();
} catch (...) {
callback.rethrow();
}
}});
auto state = std::make_shared<State>(makeRequest(path));
state->tryDownload = [callback, state, this]() {
getDownloader()->enqueueDownload(state->request,
{[callback, state, this](std::future<DownloadResult> result) {
try {
callback(result.get().data);
} catch (DownloadError & e) {
if (e.error == Downloader::NotFound || e.error == Downloader::Forbidden)
return callback(std::shared_ptr<std::string>());
++state->attempt;
if (state->attempt < state->request.tries && e.isTransient()) {
auto ms = retrySleepTime(state->attempt);
warn("%s; retrying in %d ms", e.what(), ms);
/* We can't sleep here because that would
block the download thread. So use a
separate thread for sleeping. */
std::thread([state, ms]() {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
state->tryDownload();
}).detach();
} else {
maybeDisable();
callback.rethrow();
}
} catch (...) {
callback.rethrow();
}
}});
};
state->tryDownload();
}
};