libstore: exponential backoff for downloads
Closes #932 `connect-timeout` gets replaced by an exponential backoff for the download timeout where the initial value is controlled by the setting `initial-connect-timeout`. Per iteration, the upper limit of the timeout is increased set to timeout := min(max_connect_timeout, initial_connect_timeout * 2^i) I decided to move the entire timeout / tracking of attempts into its own class to not make the filetransfer implementation more complex. Also, that allows us to write unit-tests for it. Setting `--download-attempts` to `0` is forbidden now and an exception will be thrown. For `--offline` we set it to `1`, the behavior is equivalent to what it was before: whether the max tries were exceeded is only checked after the first download exception got thrown, i.e. there's still one attempt being made. The end-result - with timeouts being caused by a wrongly set proxy - looks like this: $ env HTTPS_PROXY=1.1.1.1 nix store ping --store https://example.com warning: error: unable to download 'https://example.com/nix-cache-info': Connection timed out after 5006 milliseconds (curl error code=28); retrying in 422ms ms (attempt 1/5) warning: error: unable to download 'https://example.com/nix-cache-info': Connection timed out after 10010 milliseconds (curl error code=28); retrying in 1003ms ms (attempt 2/5) warning: error: unable to download 'https://example.com/nix-cache-info': Connection timed out after 20020 milliseconds (curl error code=28); retrying in 2018ms ms (attempt 3/5) warning: error: unable to download 'https://example.com/nix-cache-info': Connection timed out after 40007 milliseconds (curl error code=28); retrying in 4087ms ms (attempt 4/5) error: unable to download 'https://example.com/nix-cache-info': Connection timed out after 80074 milliseconds (curl error code=28) Change-Id: I9e8d08d78275bcf60080d663febc9e075243d36b
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <lix/libutil/backoff.hh>
|
||||
|
||||
namespace nix {
|
||||
TEST(Backoff, defaults)
|
||||
{
|
||||
auto initial = 5;
|
||||
auto backoff = backoffTimeouts(
|
||||
5, std::chrono::seconds(300), std::chrono::seconds(initial), std::chrono::milliseconds(1000)
|
||||
);
|
||||
|
||||
BackoffTiming timings = *backoff.next();
|
||||
ASSERT_EQ(10000, timings.downloadTimeout.count());
|
||||
ASSERT_LE(1500, timings.waitTime.count());
|
||||
ASSERT_GE(2500, timings.waitTime.count());
|
||||
|
||||
timings = *backoff.next();
|
||||
ASSERT_EQ(20000, timings.downloadTimeout.count());
|
||||
ASSERT_LE(3500, timings.waitTime.count());
|
||||
ASSERT_GE(4500, timings.waitTime.count());
|
||||
|
||||
ASSERT_TRUE(backoff.next().has_value());
|
||||
|
||||
timings = *backoff.next();
|
||||
ASSERT_EQ(80000, timings.downloadTimeout.count());
|
||||
ASSERT_LE(15500, timings.waitTime.count());
|
||||
ASSERT_GE(16500, timings.waitTime.count());
|
||||
|
||||
ASSERT_FALSE(backoff.next().has_value());
|
||||
}
|
||||
|
||||
TEST(Backoff, capped)
|
||||
{
|
||||
auto initial = 10;
|
||||
auto upper = 300;
|
||||
auto backoff = backoffTimeouts(
|
||||
7,
|
||||
std::chrono::seconds(upper),
|
||||
std::chrono::seconds(initial),
|
||||
std::chrono::milliseconds(1000)
|
||||
);
|
||||
|
||||
BackoffTiming timings = *backoff.next();
|
||||
*backoff.next();
|
||||
*backoff.next();
|
||||
*backoff.next();
|
||||
*backoff.next();
|
||||
timings = *backoff.next();
|
||||
ASSERT_EQ(300000, timings.downloadTimeout.count());
|
||||
ASSERT_FALSE(backoff.next().has_value());
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ libutil_tests_sources = files(
|
||||
'libutil/async-collect.cc',
|
||||
'libutil/async-io.cc',
|
||||
'libutil/async-semaphore.cc',
|
||||
'libutil/backoff.cc',
|
||||
'libutil/canon-path.cc',
|
||||
'libutil/checked-arithmetic.cc',
|
||||
'libutil/chunked-vector.cc',
|
||||
|
||||
Reference in New Issue
Block a user