2012-06-29 22:28:52 +00:00
|
|
|
|
#! @perl@ -w @perlFlags@
|
|
|
|
|
|
2012-07-06 04:30:40 +00:00
|
|
|
|
use DBI;
|
2012-07-02 16:42:58 +00:00
|
|
|
|
use File::Basename;
|
2012-07-06 04:30:40 +00:00
|
|
|
|
use IO::Select;
|
2012-06-29 22:28:52 +00:00
|
|
|
|
use Nix::Config;
|
|
|
|
|
use Nix::Store;
|
2012-07-30 21:09:36 +00:00
|
|
|
|
use Nix::Utils;
|
2012-07-06 04:30:40 +00:00
|
|
|
|
use WWW::Curl::Easy;
|
|
|
|
|
use WWW::Curl::Multi;
|
2012-07-31 22:56:22 +00:00
|
|
|
|
use List::MoreUtils qw(any);
|
2012-07-06 04:30:40 +00:00
|
|
|
|
use strict;
|
2012-06-29 22:28:52 +00:00
|
|
|
|
|
2012-07-02 01:55:36 +00:00
|
|
|
|
|
2012-07-09 14:57:28 +00:00
|
|
|
|
Nix::Config::readConfig;
|
2012-07-02 01:55:36 +00:00
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
my @caches;
|
|
|
|
|
my $gotCaches = 0;
|
2012-07-09 14:57:28 +00:00
|
|
|
|
|
|
|
|
|
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 150);
|
|
|
|
|
$maxParallelRequests = 1 if $maxParallelRequests < 1;
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
|
2012-07-17 20:19:40 +00:00
|
|
|
|
my $debug = ($ENV{"NIX_DEBUG_SUBST"} // "") eq 1;
|
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence);
|
2012-07-03 21:29:33 +00:00
|
|
|
|
|
2012-07-06 04:30:40 +00:00
|
|
|
|
my $curlm = WWW::Curl::Multi->new;
|
|
|
|
|
my $activeRequests = 0;
|
|
|
|
|
my $curlIdCount = 1;
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
my %requests;
|
|
|
|
|
my %scheduled;
|
2012-07-09 14:57:28 +00:00
|
|
|
|
my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
|
2012-07-06 04:30:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub addRequest {
|
2012-07-11 21:53:20 +00:00
|
|
|
|
my ($storePath, $url, $head) = @_;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-06 04:30:40 +00:00
|
|
|
|
my $curl = WWW::Curl::Easy->new;
|
|
|
|
|
my $curlId = $curlIdCount++;
|
2012-07-17 20:19:40 +00:00
|
|
|
|
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET" };
|
2012-07-06 04:30:40 +00:00
|
|
|
|
|
|
|
|
|
$curl->setopt(CURLOPT_PRIVATE, $curlId);
|
|
|
|
|
$curl->setopt(CURLOPT_URL, $url);
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
$curl->setopt(CURLOPT_WRITEDATA, \$requests{$curlId}->{content});
|
2012-07-06 04:30:40 +00:00
|
|
|
|
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
|
|
|
|
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
2012-07-11 22:05:30 +00:00
|
|
|
|
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
|
2012-07-11 21:53:20 +00:00
|
|
|
|
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
2012-07-26 21:11:11 +00:00
|
|
|
|
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
2012-07-06 04:30:40 +00:00
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
if ($activeRequests >= $maxParallelRequests) {
|
|
|
|
|
$scheduled{$curlId} = 1;
|
|
|
|
|
} else {
|
|
|
|
|
$curlm->add_handle($curl);
|
|
|
|
|
$activeRequests++;
|
|
|
|
|
}
|
2012-07-06 04:30:40 +00:00
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
return $requests{$curlId};
|
2012-07-06 04:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub processRequests {
|
|
|
|
|
while ($activeRequests) {
|
|
|
|
|
my ($rfds, $wfds, $efds) = $curlm->fdset();
|
|
|
|
|
#print STDERR "R = @{$rfds}, W = @{$wfds}, E = @{$efds}\n";
|
|
|
|
|
|
|
|
|
|
# Sleep until we can read or write some data.
|
|
|
|
|
if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) {
|
|
|
|
|
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 0.1);
|
|
|
|
|
}
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-06 04:30:40 +00:00
|
|
|
|
if ($curlm->perform() != $activeRequests) {
|
|
|
|
|
while (my ($id, $result) = $curlm->info_read) {
|
|
|
|
|
if ($id) {
|
2012-07-17 20:19:40 +00:00
|
|
|
|
my $request = $requests{$id} or die;
|
|
|
|
|
my $handle = $request->{handle};
|
|
|
|
|
$request->{result} = $result;
|
2012-07-26 21:11:11 +00:00
|
|
|
|
$request->{httpStatus} = $handle->getinfo(CURLINFO_RESPONSE_CODE);
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-17 20:19:40 +00:00
|
|
|
|
print STDERR "$request->{type} on $request->{url} [$request->{result}, $request->{httpStatus}]\n" if $debug;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-06 04:30:40 +00:00
|
|
|
|
$activeRequests--;
|
2012-07-17 20:19:40 +00:00
|
|
|
|
delete $request->{handle};
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
|
|
|
|
|
if (scalar(keys %scheduled) > 0) {
|
|
|
|
|
my $id2 = (keys %scheduled)[0];
|
|
|
|
|
$curlm->add_handle($requests{$id2}->{handle});
|
|
|
|
|
$activeRequests++;
|
|
|
|
|
delete $scheduled{$id2};
|
|
|
|
|
}
|
2012-07-06 04:30:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-03 21:29:33 +00:00
|
|
|
|
|
|
|
|
|
sub initCache {
|
|
|
|
|
my $dbPath = "$Nix::Config::stateDir/binary-cache-v1.sqlite";
|
|
|
|
|
|
|
|
|
|
# Open/create the database.
|
|
|
|
|
$dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
|
|
|
|
|
or die "cannot open database `$dbPath'";
|
|
|
|
|
$dbh->{RaiseError} = 1;
|
|
|
|
|
$dbh->{PrintError} = 0;
|
|
|
|
|
|
|
|
|
|
$dbh->do("pragma synchronous = off"); # we can always reproduce the cache
|
|
|
|
|
$dbh->do("pragma journal_mode = truncate");
|
|
|
|
|
|
|
|
|
|
# Initialise the database schema, if necessary.
|
|
|
|
|
$dbh->do(<<EOF);
|
|
|
|
|
create table if not exists BinaryCaches (
|
|
|
|
|
id integer primary key autoincrement not null,
|
2012-07-27 22:16:05 +00:00
|
|
|
|
url text unique not null,
|
|
|
|
|
timestamp integer not null,
|
|
|
|
|
storeDir text not null,
|
|
|
|
|
wantMassQuery integer not null
|
2012-07-03 21:29:33 +00:00
|
|
|
|
);
|
|
|
|
|
EOF
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-03 21:29:33 +00:00
|
|
|
|
$dbh->do(<<EOF);
|
|
|
|
|
create table if not exists NARs (
|
|
|
|
|
cache integer not null,
|
|
|
|
|
storePath text not null,
|
|
|
|
|
url text not null,
|
|
|
|
|
compression text not null,
|
|
|
|
|
fileHash text,
|
|
|
|
|
fileSize integer,
|
|
|
|
|
narHash text,
|
|
|
|
|
narSize integer,
|
|
|
|
|
refs text,
|
|
|
|
|
deriver text,
|
|
|
|
|
system text,
|
|
|
|
|
timestamp integer not null,
|
|
|
|
|
primary key (cache, storePath),
|
|
|
|
|
foreign key (cache) references BinaryCaches(id) on delete cascade
|
|
|
|
|
);
|
|
|
|
|
EOF
|
|
|
|
|
|
2012-07-03 22:54:46 +00:00
|
|
|
|
$dbh->do(<<EOF);
|
2012-07-11 21:53:20 +00:00
|
|
|
|
create table if not exists NARExistence (
|
2012-07-03 22:54:46 +00:00
|
|
|
|
cache integer not null,
|
|
|
|
|
storePath text not null,
|
2012-07-11 21:53:20 +00:00
|
|
|
|
exist integer not null,
|
2012-07-03 22:54:46 +00:00
|
|
|
|
timestamp integer not null,
|
|
|
|
|
primary key (cache, storePath),
|
|
|
|
|
foreign key (cache) references BinaryCaches(id) on delete cascade
|
|
|
|
|
);
|
|
|
|
|
EOF
|
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
$queryCache = $dbh->prepare("select id, storeDir, wantMassQuery from BinaryCaches where url = ?") or die;
|
|
|
|
|
|
2012-07-03 21:29:33 +00:00
|
|
|
|
$insertNAR = $dbh->prepare(
|
|
|
|
|
"insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, " .
|
|
|
|
|
"narSize, refs, deriver, system, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
|
|
|
|
|
|
|
|
|
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
|
2012-07-03 22:54:46 +00:00
|
|
|
|
|
2012-07-11 21:53:20 +00:00
|
|
|
|
$insertNARExistence = $dbh->prepare(
|
|
|
|
|
"insert or replace into NARExistence(cache, storePath, exist, timestamp) values (?, ?, ?, ?)") or die;
|
2012-07-03 22:54:46 +00:00
|
|
|
|
|
2012-07-11 21:53:20 +00:00
|
|
|
|
$queryNARExistence = $dbh->prepare("select exist from NARExistence where cache = ? and storePath = ?") or die;
|
2012-07-03 21:29:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-29 22:28:52 +00:00
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
sub getAvailableCaches {
|
|
|
|
|
return if $gotCaches;
|
|
|
|
|
$gotCaches = 1;
|
2012-07-11 21:53:20 +00:00
|
|
|
|
|
2012-07-31 22:56:22 +00:00
|
|
|
|
sub strToList {
|
|
|
|
|
my ($s) = @_;
|
|
|
|
|
return map { s/\/+$//; $_ } split(/ /, $s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my @urls = strToList
|
|
|
|
|
($Nix::Config::config{"binary-caches"}
|
|
|
|
|
// ($Nix::Config::storeDir eq "/nix/store" ? "http://nixos.org/binary-cache" : ""));
|
|
|
|
|
|
|
|
|
|
# Allow Nix daemon users to override the binary caches to a subset
|
|
|
|
|
# of those listed in the config file. Note that ‘untrusted-*’
|
|
|
|
|
# denotes options passed by the client.
|
|
|
|
|
if (defined $Nix::Config::config{"untrusted-binary-caches"}) {
|
|
|
|
|
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"};
|
|
|
|
|
my @trustedUrls = (@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
|
|
|
|
|
@urls = ();
|
|
|
|
|
foreach my $url (@untrustedUrls) {
|
2012-08-01 15:19:24 +00:00
|
|
|
|
die "binary cache ‘$url’ is not trusted (please add it to ‘trusted-binary-caches’ in $Nix::Config::confDir/nix.conf)\n"
|
|
|
|
|
unless any { $url eq $_ } @trustedUrls;
|
|
|
|
|
push @urls, $url;
|
2012-07-31 22:56:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-11 21:53:20 +00:00
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
foreach my $url (@urls) {
|
2012-07-11 21:53:20 +00:00
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
# FIXME: not atomic.
|
|
|
|
|
$queryCache->execute($url);
|
|
|
|
|
my $res = $queryCache->fetchrow_hashref();
|
|
|
|
|
if (defined $res) {
|
|
|
|
|
next if $res->{storeDir} ne $Nix::Config::storeDir;
|
|
|
|
|
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $res->{wantMassQuery} };
|
|
|
|
|
next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Get the cache info file.
|
|
|
|
|
my $request = addRequest(undef, $url . "/nix-cache-info");
|
|
|
|
|
processRequests;
|
|
|
|
|
|
|
|
|
|
if ($request->{result} != 0) {
|
|
|
|
|
print STDERR "could not download ‘$request->{url}’ (" .
|
|
|
|
|
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
|
|
|
|
next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $storeDir = "/nix/store";
|
|
|
|
|
my $wantMassQuery = 0;
|
|
|
|
|
foreach my $line (split "\n", $request->{content}) {
|
|
|
|
|
unless ($line =~ /^(.*): (.*)$/) {
|
|
|
|
|
print STDERR "bad cache info file ‘$request->{url}’\n";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
if ($1 eq "StoreDir") { $storeDir = $2; }
|
|
|
|
|
elsif ($1 eq "WantMassQuery") { $wantMassQuery = int($2); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$dbh->do("insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery) values (?, ?, ?, ?)",
|
|
|
|
|
{}, $url, time(), $storeDir, $wantMassQuery);
|
|
|
|
|
my $id = $dbh->last_insert_id("", "", "", "");
|
|
|
|
|
next if $storeDir ne $Nix::Config::storeDir;
|
|
|
|
|
push @caches, { id => $id, url => $url, wantMassQuery => $wantMassQuery };
|
|
|
|
|
}
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
}
|
2012-07-03 22:54:46 +00:00
|
|
|
|
|
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
sub processNARInfo {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
my ($storePath, $cache, $request) = @_;
|
2012-07-06 04:30:40 +00:00
|
|
|
|
|
2012-07-26 21:11:11 +00:00
|
|
|
|
if ($request->{result} != 0) {
|
|
|
|
|
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
print STDERR "could not download ‘$request->{url}’ (" .
|
2012-07-06 04:30:40 +00:00
|
|
|
|
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
2012-07-03 22:54:46 +00:00
|
|
|
|
} else {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
$insertNARExistence->execute($cache->{id}, basename($storePath), 0, time())
|
2012-07-26 21:11:11 +00:00
|
|
|
|
unless $request->{url} =~ /^file:/;
|
2012-07-03 22:54:46 +00:00
|
|
|
|
}
|
2012-06-29 22:28:52 +00:00
|
|
|
|
return undef;
|
|
|
|
|
}
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-03 22:35:39 +00:00
|
|
|
|
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system);
|
|
|
|
|
my $compression = "bzip2";
|
2012-06-29 22:28:52 +00:00
|
|
|
|
my @refs;
|
2012-07-06 04:30:40 +00:00
|
|
|
|
foreach my $line (split "\n", $request->{content}) {
|
2012-07-17 20:19:40 +00:00
|
|
|
|
unless ($line =~ /^(.*): (.*)$/) {
|
|
|
|
|
print STDERR "bad NAR info file ‘$request->{url}’\n";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
2012-06-29 22:28:52 +00:00
|
|
|
|
if ($1 eq "StorePath") { $storePath2 = $2; }
|
|
|
|
|
elsif ($1 eq "URL") { $url = $2; }
|
2012-07-01 22:46:38 +00:00
|
|
|
|
elsif ($1 eq "Compression") { $compression = $2; }
|
|
|
|
|
elsif ($1 eq "FileHash") { $fileHash = $2; }
|
|
|
|
|
elsif ($1 eq "FileSize") { $fileSize = int($2); }
|
2012-06-29 22:28:52 +00:00
|
|
|
|
elsif ($1 eq "NarHash") { $narHash = $2; }
|
|
|
|
|
elsif ($1 eq "NarSize") { $narSize = int($2); }
|
|
|
|
|
elsif ($1 eq "References") { @refs = split / /, $2; }
|
|
|
|
|
elsif ($1 eq "Deriver") { $deriver = $2; }
|
2012-07-03 22:35:39 +00:00
|
|
|
|
elsif ($1 eq "System") { $system = $2; }
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
2012-07-03 21:29:33 +00:00
|
|
|
|
return undef if $storePath ne $storePath2;
|
2012-07-02 22:53:04 +00:00
|
|
|
|
if ($storePath ne $storePath2 || !defined $url || !defined $narHash) {
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
print STDERR "bad NAR info file ‘$request->{url}’\n";
|
2012-07-02 22:53:04 +00:00
|
|
|
|
return undef;
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
2012-07-18 15:01:17 +00:00
|
|
|
|
|
2012-07-03 22:35:39 +00:00
|
|
|
|
# Cache the result.
|
|
|
|
|
$insertNAR->execute(
|
2012-07-27 22:16:05 +00:00
|
|
|
|
$cache->{id}, basename($storePath), $url, $compression, $fileHash, $fileSize,
|
2012-07-26 21:11:11 +00:00
|
|
|
|
$narHash, $narSize, join(" ", @refs), $deriver, $system, time())
|
|
|
|
|
unless $request->{url} =~ /^file:/;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-06-29 22:28:52 +00:00
|
|
|
|
return
|
|
|
|
|
{ url => $url
|
2012-07-03 22:35:39 +00:00
|
|
|
|
, compression => $compression
|
2012-06-29 22:28:52 +00:00
|
|
|
|
, fileHash => $fileHash
|
|
|
|
|
, fileSize => $fileSize
|
|
|
|
|
, narHash => $narHash
|
|
|
|
|
, narSize => $narSize
|
2012-07-03 21:29:33 +00:00
|
|
|
|
, refs => [ @refs ]
|
|
|
|
|
, deriver => $deriver
|
2012-07-03 22:35:39 +00:00
|
|
|
|
, system => $system
|
2012-07-03 21:29:33 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-07-11 14:13:16 +00:00
|
|
|
|
sub getCachedInfoFrom {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
my ($storePath, $cache) = @_;
|
2012-07-03 21:29:33 +00:00
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
$queryNAR->execute($cache->{id}, basename($storePath));
|
2012-07-03 21:29:33 +00:00
|
|
|
|
my $res = $queryNAR->fetchrow_hashref();
|
2012-07-03 22:35:39 +00:00
|
|
|
|
return undef unless defined $res;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
|
|
|
|
return
|
2012-07-03 21:29:33 +00:00
|
|
|
|
{ url => $res->{url}
|
|
|
|
|
, compression => $res->{compression}
|
|
|
|
|
, fileHash => $res->{fileHash}
|
|
|
|
|
, fileSize => $res->{fileSize}
|
|
|
|
|
, narHash => $res->{narHash}
|
|
|
|
|
, narSize => $res->{narSize}
|
|
|
|
|
, refs => [ split " ", $res->{refs} ]
|
|
|
|
|
, deriver => $res->{deriver}
|
|
|
|
|
} if defined $res;
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 01:55:36 +00:00
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
sub negativeHit {
|
|
|
|
|
my ($storePath, $cache) = @_;
|
|
|
|
|
$queryNARExistence->execute($cache->{id}, basename($storePath));
|
|
|
|
|
my $res = $queryNARExistence->fetchrow_hashref();
|
|
|
|
|
return defined $res && $res->{exist} == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub positiveHit {
|
|
|
|
|
my ($storePath, $cache) = @_;
|
|
|
|
|
return 1 if defined getCachedInfoFrom($storePath, $cache);
|
|
|
|
|
$queryNARExistence->execute($cache->{id}, basename($storePath));
|
|
|
|
|
my $res = $queryNARExistence->fetchrow_hashref();
|
|
|
|
|
return defined $res && $res->{exist} == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
sub printInfo {
|
|
|
|
|
my ($storePath, $info) = @_;
|
|
|
|
|
print "$storePath\n";
|
|
|
|
|
print $info->{deriver} ? "$Nix::Config::storeDir/$info->{deriver}" : "", "\n";
|
|
|
|
|
print scalar @{$info->{refs}}, "\n";
|
|
|
|
|
print "$Nix::Config::storeDir/$_\n" foreach @{$info->{refs}};
|
|
|
|
|
print $info->{fileSize} || 0, "\n";
|
|
|
|
|
print $info->{narSize} || 0, "\n";
|
|
|
|
|
}
|
2012-06-29 22:28:52 +00:00
|
|
|
|
|
|
|
|
|
|
2012-07-08 22:39:07 +00:00
|
|
|
|
sub infoUrl {
|
|
|
|
|
my ($binaryCacheUrl, $storePath) = @_;
|
|
|
|
|
my $pathHash = substr(basename($storePath), 0, 32);
|
|
|
|
|
my $infoUrl = "$binaryCacheUrl/$pathHash.narinfo";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
sub printInfoParallel {
|
|
|
|
|
my @paths = @_;
|
|
|
|
|
|
|
|
|
|
# First print all paths for which we have cached info.
|
|
|
|
|
my @left;
|
|
|
|
|
foreach my $storePath (@paths) {
|
|
|
|
|
my $found = 0;
|
2012-07-27 22:16:05 +00:00
|
|
|
|
foreach my $cache (@caches) {
|
|
|
|
|
my $info = getCachedInfoFrom($storePath, $cache);
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
if (defined $info) {
|
|
|
|
|
printInfo($storePath, $info);
|
|
|
|
|
$found = 1;
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
push @left, $storePath if !$found;
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
return if scalar @left == 0;
|
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
foreach my $cache (@caches) {
|
2012-07-03 22:35:39 +00:00
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
my @left2;
|
|
|
|
|
%requests = ();
|
|
|
|
|
foreach my $storePath (@left) {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
if (negativeHit($storePath, $cache)) {
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
push @left2, $storePath;
|
|
|
|
|
next;
|
|
|
|
|
}
|
2012-07-27 22:16:05 +00:00
|
|
|
|
addRequest($storePath, infoUrl($cache->{url}, $storePath));
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
processRequests;
|
|
|
|
|
|
|
|
|
|
foreach my $request (values %requests) {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
my $info = processNARInfo($request->{storePath}, $cache, $request);
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
if (defined $info) {
|
|
|
|
|
printInfo($request->{storePath}, $info);
|
|
|
|
|
} else {
|
|
|
|
|
push @left2, $request->{storePath};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@left = @left2;
|
|
|
|
|
}
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 01:55:36 +00:00
|
|
|
|
|
2012-07-11 21:53:20 +00:00
|
|
|
|
sub printSubstitutablePaths {
|
|
|
|
|
my @paths = @_;
|
|
|
|
|
|
|
|
|
|
# First look for paths that have cached info.
|
|
|
|
|
my @left;
|
|
|
|
|
foreach my $storePath (@paths) {
|
|
|
|
|
my $found = 0;
|
2012-07-27 22:16:05 +00:00
|
|
|
|
foreach my $cache (@caches) {
|
|
|
|
|
next unless $cache->{wantMassQuery};
|
|
|
|
|
if (positiveHit($storePath, $cache)) {
|
2012-07-11 21:53:20 +00:00
|
|
|
|
print "$storePath\n";
|
|
|
|
|
$found = 1;
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
push @left, $storePath if !$found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return if scalar @left == 0;
|
|
|
|
|
|
|
|
|
|
# For remaining paths, do HEAD requests.
|
2012-07-27 22:16:05 +00:00
|
|
|
|
foreach my $cache (@caches) {
|
|
|
|
|
next unless $cache->{wantMassQuery};
|
2012-07-11 21:53:20 +00:00
|
|
|
|
my @left2;
|
|
|
|
|
%requests = ();
|
|
|
|
|
foreach my $storePath (@left) {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
if (negativeHit($storePath, $cache)) {
|
2012-07-11 21:53:20 +00:00
|
|
|
|
push @left2, $storePath;
|
|
|
|
|
next;
|
|
|
|
|
}
|
2012-07-27 22:16:05 +00:00
|
|
|
|
addRequest($storePath, infoUrl($cache->{url}, $storePath), 1);
|
2012-07-11 21:53:20 +00:00
|
|
|
|
}
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-11 21:53:20 +00:00
|
|
|
|
processRequests;
|
|
|
|
|
|
|
|
|
|
foreach my $request (values %requests) {
|
2012-07-26 21:11:11 +00:00
|
|
|
|
if ($request->{result} != 0) {
|
|
|
|
|
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
2012-07-11 21:53:20 +00:00
|
|
|
|
print STDERR "could not check ‘$request->{url}’ (" .
|
|
|
|
|
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
|
|
|
|
} else {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time())
|
2012-07-26 21:11:11 +00:00
|
|
|
|
unless $request->{url} =~ /^file:/;
|
2012-07-11 21:53:20 +00:00
|
|
|
|
}
|
|
|
|
|
push @left2, $request->{storePath};
|
|
|
|
|
} else {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time())
|
2012-07-26 21:11:11 +00:00
|
|
|
|
unless $request->{url} =~ /^file:/;
|
2012-07-11 21:53:20 +00:00
|
|
|
|
print "$request->{storePath}\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@left = @left2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-06-29 22:28:52 +00:00
|
|
|
|
sub downloadBinary {
|
|
|
|
|
my ($storePath) = @_;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
foreach my $cache (@caches) {
|
|
|
|
|
my $info = getCachedInfoFrom($storePath, $cache);
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
|
|
|
|
|
unless (defined $info) {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
next if negativeHit($storePath, $cache);
|
|
|
|
|
my $request = addRequest($storePath, infoUrl($cache->{url}, $storePath));
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
processRequests;
|
2012-07-27 22:16:05 +00:00
|
|
|
|
$info = processNARInfo($storePath, $cache, $request);
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next unless defined $info;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
my $decompressor;
|
|
|
|
|
if ($info->{compression} eq "bzip2") { $decompressor = "$Nix::Config::bzip2 -d"; }
|
|
|
|
|
elsif ($info->{compression} eq "xz") { $decompressor = "$Nix::Config::xz -d"; }
|
|
|
|
|
else {
|
|
|
|
|
print STDERR "unknown compression method ‘$info->{compression}’\n";
|
|
|
|
|
next;
|
|
|
|
|
}
|
2012-07-27 22:16:05 +00:00
|
|
|
|
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
|
2012-07-18 15:01:17 +00:00
|
|
|
|
print STDERR "\n*** Downloading ‘$url’ into ‘$storePath’...\n";
|
2012-07-30 21:09:36 +00:00
|
|
|
|
Nix::Utils::checkURL $url;
|
2012-07-27 14:47:36 +00:00
|
|
|
|
if (system("$Nix::Config::curl --fail --location --insecure '$url' | $decompressor | $Nix::Config::binDir/nix-store --restore $storePath") != 0) {
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
die "download of `$info->{url}' failed" . ($! ? ": $!" : "") . "\n" unless $? == 0;
|
|
|
|
|
next;
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
2012-07-27 16:16:02 +00:00
|
|
|
|
|
|
|
|
|
# Tell Nix about the expected hash so it can verify it.
|
|
|
|
|
print "$info->{narHash}\n";
|
|
|
|
|
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
print STDERR "\n";
|
2012-07-27 22:16:05 +00:00
|
|
|
|
return;
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-27 22:16:05 +00:00
|
|
|
|
print STDERR "could not download ‘$storePath’ from any binary cache\n";
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 01:55:36 +00:00
|
|
|
|
|
2012-07-03 21:29:33 +00:00
|
|
|
|
initCache();
|
|
|
|
|
|
|
|
|
|
|
2012-06-29 22:28:52 +00:00
|
|
|
|
if ($ARGV[0] eq "--query") {
|
|
|
|
|
|
|
|
|
|
while (<STDIN>) {
|
2012-07-27 22:16:05 +00:00
|
|
|
|
getAvailableCaches;
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
chomp;
|
|
|
|
|
my ($cmd, @args) = split " ", $_;
|
2012-07-27 13:59:18 +00:00
|
|
|
|
|
2012-06-29 22:28:52 +00:00
|
|
|
|
if ($cmd eq "have") {
|
2012-07-11 21:53:20 +00:00
|
|
|
|
printSubstitutablePaths(@args);
|
|
|
|
|
print "\n";
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elsif ($cmd eq "info") {
|
download-from-binary-cache: parallelise fetching of NAR info files
Getting substitute information using the binary cache substituter has
non-trivial latency overhead. A package or NixOS system configuration
can have hundreds of dependencies, and in the worst case (when the
local info cache is empty) we have to do a separate HTTP request for
each of these. If the ping time to the server is t, getting N info
files will take tN seconds; e.g., with a ping time of 0.1s to
nixos.org, sequentially downloading 1000 info files (a typical NixOS
config) will take at least 100 seconds.
To fix this problem, the binary cache substituter can now perform
requests in parallel. This required changing the substituter
interface to support a function querySubstitutablePathInfos() that
queries multiple paths at the same time, and rewriting queryMissing()
to take advantage of parallelism. (Due to local caching,
parallelising queryMissing() is sufficient for most use cases, since
it's almost always called before building a derivation and thus fills
the local info cache.)
For example, parallelism speeds up querying all 1056 paths in a
particular NixOS system configuration from 116s to 2.6s. It works so
well because the eccentricity of the top-level derivation in the
dependency graph is only 9. So we only need 10 round-trips (when
using an unlimited number of parallel connections) to get everything.
Currently we do a maximum of 150 parallel connections to the server.
Thus it's important that the binary cache server (e.g. nixos.org) has
a high connection limit. Alternatively we could use HTTP pipelining,
but WWW::Curl doesn't support it and libcurl has a hard-coded limit of
5 requests per pipeline.
2012-07-06 23:08:20 +00:00
|
|
|
|
printInfoParallel(@args);
|
|
|
|
|
print "\n";
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else { die "unknown command `$cmd'"; }
|
|
|
|
|
|
|
|
|
|
flush STDOUT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elsif ($ARGV[0] eq "--substitute") {
|
|
|
|
|
my $storePath = $ARGV[1] or die;
|
2012-07-27 22:16:05 +00:00
|
|
|
|
getAvailableCaches;
|
|
|
|
|
downloadBinary($storePath);
|
2012-06-29 22:28:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
die;
|
|
|
|
|
}
|