download-from-binary-cache: Use HEAD requests if possible
In "nix-env -qas", we don't need the substitute info, we just need to know if it exists. This can be done using a HTTP HEAD request, which saves bandwidth. Note however that curl currently has a bug that prevents it from reusing HTTP connections if HEAD requests return a 404: https://sourceforge.net/tracker/?func=detail&aid=3542731&group_id=976&atid=100976 Without the patch attached to the issue, using HEAD is actually quite a bit slower than GET.
This commit is contained in:
parent
09a6321aeb
commit
b74d92755d
1 changed files with 79 additions and 12 deletions
|
@ -18,7 +18,7 @@ my @binaryCacheUrls = map { s/\/+$//; $_ } split(/ /,
|
||||||
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 150);
|
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 150);
|
||||||
$maxParallelRequests = 1 if $maxParallelRequests < 1;
|
$maxParallelRequests = 1 if $maxParallelRequests < 1;
|
||||||
|
|
||||||
my ($dbh, $insertNAR, $queryNAR, $insertNegativeNAR, $queryNegativeNAR);
|
my ($dbh, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence);
|
||||||
my %cacheIds;
|
my %cacheIds;
|
||||||
|
|
||||||
my $curlm = WWW::Curl::Multi->new;
|
my $curlm = WWW::Curl::Multi->new;
|
||||||
|
@ -30,7 +30,7 @@ my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
|
||||||
|
|
||||||
|
|
||||||
sub addRequest {
|
sub addRequest {
|
||||||
my ($storePath, $url) = @_;
|
my ($storePath, $url, $head) = @_;
|
||||||
|
|
||||||
my $curl = WWW::Curl::Easy->new;
|
my $curl = WWW::Curl::Easy->new;
|
||||||
my $curlId = $curlIdCount++;
|
my $curlId = $curlIdCount++;
|
||||||
|
@ -41,6 +41,7 @@ sub addRequest {
|
||||||
$curl->setopt(CURLOPT_WRITEDATA, \$requests{$curlId}->{content});
|
$curl->setopt(CURLOPT_WRITEDATA, \$requests{$curlId}->{content});
|
||||||
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
||||||
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
||||||
|
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
||||||
|
|
||||||
if ($activeRequests >= $maxParallelRequests) {
|
if ($activeRequests >= $maxParallelRequests) {
|
||||||
$scheduled{$curlId} = 1;
|
$scheduled{$curlId} = 1;
|
||||||
|
@ -127,9 +128,10 @@ EOF
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
$dbh->do(<<EOF);
|
$dbh->do(<<EOF);
|
||||||
create table if not exists NegativeNARs (
|
create table if not exists NARExistence (
|
||||||
cache integer not null,
|
cache integer not null,
|
||||||
storePath text not null,
|
storePath text not null,
|
||||||
|
exist integer not null,
|
||||||
timestamp integer not null,
|
timestamp integer not null,
|
||||||
primary key (cache, storePath),
|
primary key (cache, storePath),
|
||||||
foreign key (cache) references BinaryCaches(id) on delete cascade
|
foreign key (cache) references BinaryCaches(id) on delete cascade
|
||||||
|
@ -142,17 +144,28 @@ EOF
|
||||||
|
|
||||||
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
|
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
|
||||||
|
|
||||||
$insertNegativeNAR = $dbh->prepare(
|
$insertNARExistence = $dbh->prepare(
|
||||||
"insert or replace into NegativeNARs(cache, storePath, timestamp) values (?, ?, ?)") or die;
|
"insert or replace into NARExistence(cache, storePath, exist, timestamp) values (?, ?, ?, ?)") or die;
|
||||||
|
|
||||||
$queryNegativeNAR = $dbh->prepare("select 1 from NegativeNARs where cache = ? and storePath = ?") or die;
|
$queryNARExistence = $dbh->prepare("select exist from NARExistence where cache = ? and storePath = ?") or die;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sub negativeHit {
|
sub negativeHit {
|
||||||
my ($storePath, $binaryCacheUrl) = @_;
|
my ($storePath, $binaryCacheUrl) = @_;
|
||||||
$queryNegativeNAR->execute(getCacheId($binaryCacheUrl), basename($storePath));
|
$queryNARExistence->execute(getCacheId($binaryCacheUrl), basename($storePath));
|
||||||
return @{$queryNegativeNAR->fetchall_arrayref()} != 0;
|
my $res = $queryNARExistence->fetchrow_hashref();
|
||||||
|
return defined $res && $res->{exist} == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub positiveHit {
|
||||||
|
my ($storePath, $binaryCacheUrl) = @_;
|
||||||
|
return 1 if defined getCachedInfoFrom($storePath, $binaryCacheUrl);
|
||||||
|
$queryNARExistence->execute(getCacheId($binaryCacheUrl), basename($storePath));
|
||||||
|
my $res = $queryNARExistence->fetchrow_hashref();
|
||||||
|
return defined $res && $res->{exist} == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,7 +179,7 @@ sub processNARInfo {
|
||||||
print STDERR "could not download ‘$request->{url}’ (" .
|
print STDERR "could not download ‘$request->{url}’ (" .
|
||||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||||
} else {
|
} else {
|
||||||
$insertNegativeNAR->execute($cacheId, basename($storePath), time());
|
$insertNARExistence->execute($cacheId, basename($storePath), 0, time());
|
||||||
}
|
}
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
@ -319,6 +332,61 @@ sub printInfoParallel {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub printSubstitutablePaths {
|
||||||
|
my @paths = @_;
|
||||||
|
|
||||||
|
# First look for paths that have cached info.
|
||||||
|
my @left;
|
||||||
|
foreach my $storePath (@paths) {
|
||||||
|
my $found = 0;
|
||||||
|
foreach my $binaryCacheUrl (@binaryCacheUrls) {
|
||||||
|
if (positiveHit($storePath, $binaryCacheUrl)) {
|
||||||
|
print "$storePath\n";
|
||||||
|
$found = 1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
push @left, $storePath if !$found;
|
||||||
|
}
|
||||||
|
|
||||||
|
return if scalar @left == 0;
|
||||||
|
|
||||||
|
# For remaining paths, do HEAD requests.
|
||||||
|
foreach my $binaryCacheUrl (@binaryCacheUrls) {
|
||||||
|
my $cacheId = getCacheId($binaryCacheUrl);
|
||||||
|
|
||||||
|
my @left2;
|
||||||
|
%requests = ();
|
||||||
|
foreach my $storePath (@left) {
|
||||||
|
if (negativeHit($storePath, $binaryCacheUrl)) {
|
||||||
|
push @left2, $storePath;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
addRequest($storePath, infoUrl($binaryCacheUrl, $storePath), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
processRequests;
|
||||||
|
|
||||||
|
foreach my $request (values %requests) {
|
||||||
|
if ($request->{result} != 0 || $request->{httpStatus} != 200) {
|
||||||
|
if ($request->{httpStatus} != 404) {
|
||||||
|
print STDERR "could not check ‘$request->{url}’ (" .
|
||||||
|
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||||
|
} else {
|
||||||
|
$insertNARExistence->execute($cacheId, basename($request->{storePath}), 0, time());
|
||||||
|
}
|
||||||
|
push @left2, $request->{storePath};
|
||||||
|
} else {
|
||||||
|
$insertNARExistence->execute($cacheId, basename($request->{storePath}), 1, time());
|
||||||
|
print "$request->{storePath}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@left = @left2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub downloadBinary {
|
sub downloadBinary {
|
||||||
my ($storePath) = @_;
|
my ($storePath) = @_;
|
||||||
|
|
||||||
|
@ -371,9 +439,8 @@ if ($ARGV[0] eq "--query") {
|
||||||
my ($cmd, @args) = split " ", $_;
|
my ($cmd, @args) = split " ", $_;
|
||||||
|
|
||||||
if ($cmd eq "have") {
|
if ($cmd eq "have") {
|
||||||
my $storePath = <STDIN>; chomp $storePath;
|
printSubstitutablePaths(@args);
|
||||||
# FIXME: want to give correct info here, but it's too slow.
|
print "\n";
|
||||||
print "0\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($cmd eq "info") {
|
elsif ($cmd eq "info") {
|
||||||
|
|
Loading…
Reference in a new issue