2012-06-30 00:28:52 +02:00
|
|
|
|
#! @perl@ -w @perlFlags@
|
|
|
|
|
|
2014-08-29 17:48:25 +02:00
|
|
|
|
use utf8;
|
2012-07-06 06:30:40 +02:00
|
|
|
|
use DBI;
|
2013-10-24 19:10:38 +02:00
|
|
|
|
use DBD::SQLite;
|
2012-07-02 18:42:58 +02:00
|
|
|
|
use File::Basename;
|
2012-07-06 06:30:40 +02:00
|
|
|
|
use IO::Select;
|
2012-06-30 00:28:52 +02:00
|
|
|
|
use Nix::Config;
|
|
|
|
|
use Nix::Store;
|
2012-07-30 23:09:36 +02:00
|
|
|
|
use Nix::Utils;
|
2012-10-17 22:45:04 +02:00
|
|
|
|
use Nix::Manifest;
|
2012-07-06 06:30:40 +02:00
|
|
|
|
use WWW::Curl::Easy;
|
|
|
|
|
use WWW::Curl::Multi;
|
|
|
|
|
use strict;
|
2012-06-30 00:28:52 +02:00
|
|
|
|
|
2014-11-04 14:36:17 +01:00
|
|
|
|
STDERR->autoflush(1);
|
2014-08-29 17:48:25 +02:00
|
|
|
|
binmode STDERR, ":encoding(utf8)";
|
2012-07-02 03:55:36 +02:00
|
|
|
|
|
2012-07-09 16:57:28 +02:00
|
|
|
|
Nix::Config::readConfig;
|
2012-07-02 03:55:36 +02:00
|
|
|
|
|
2012-07-28 00:16:05 +02:00
|
|
|
|
my @caches;
|
|
|
|
|
my $gotCaches = 0;
|
2012-07-09 16:57:28 +02: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-07 01:08:20 +02:00
|
|
|
|
|
2012-09-13 22:39:16 +02:00
|
|
|
|
my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
|
|
|
|
|
my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups
|
|
|
|
|
my $didExpiration = 0;
|
|
|
|
|
|
2013-06-07 15:33:44 +02:00
|
|
|
|
my $showAfter = 5; # show that we're waiting for a request after this many seconds
|
|
|
|
|
|
2013-06-07 15:35:54 +02:00
|
|
|
|
my $debug = ($Nix::Config::config{"debug-subst"} // "") eq 1 || ($Nix::Config::config{"untrusted-debug-subst"} // "") eq 1;
|
2012-07-17 22:19:40 +02:00
|
|
|
|
|
2013-04-23 12:43:28 +02:00
|
|
|
|
my $cacheFileURLs = ($ENV{"_NIX_CACHE_FILE_URLS"} // "") eq 1; # for testing
|
|
|
|
|
|
2012-09-13 22:39:16 +02:00
|
|
|
|
my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence, $expireNARExistence);
|
2012-07-03 23:29:33 +02:00
|
|
|
|
|
2012-07-06 06:30:40 +02: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-07 01:08:20 +02:00
|
|
|
|
my %requests;
|
|
|
|
|
my %scheduled;
|
2014-08-07 22:41:15 +02:00
|
|
|
|
my $caBundle = $ENV{"SSL_CERT_FILE"} // $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
|
2014-12-09 13:13:05 +01:00
|
|
|
|
$caBundle = "/etc/ssl/certs/ca-bundle.crt" if !$caBundle && -f "/etc/ssl/certs/ca-bundle.crt";
|
|
|
|
|
$caBundle = "/etc/ssl/certs/ca-certificates.crt" if !$caBundle && -f "/etc/ssl/certs/ca-certificates.crt";
|
2012-07-06 06:30:40 +02:00
|
|
|
|
|
2014-02-26 13:48:23 +01:00
|
|
|
|
my $userName = getpwuid($<) || $ENV{"USER"} or die "cannot figure out user name";
|
2012-12-21 15:00:07 +01:00
|
|
|
|
|
2014-12-09 13:15:31 +01:00
|
|
|
|
sub isTrue {
|
|
|
|
|
my ($x) = @_;
|
|
|
|
|
return $x eq "true" || $x eq "1";
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-09 20:43:12 +01:00
|
|
|
|
my $requireSignedBinaryCaches = ($Nix::Config::config{"signed-binary-caches"} // "0") ne "0";
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
|
2014-02-26 16:07:43 +01:00
|
|
|
|
my $curlConnectTimeout = int(
|
|
|
|
|
$Nix::Config::config{"untrusted-connect-timeout"} //
|
|
|
|
|
$Nix::Config::config{"connect-timeout"} //
|
|
|
|
|
$ENV{"NIX_CONNECT_TIMEOUT"} // 0);
|
2014-02-26 15:58:37 +01:00
|
|
|
|
|
2012-07-06 06:30:40 +02:00
|
|
|
|
|
|
|
|
|
sub addRequest {
|
2012-07-11 23:53:20 +02:00
|
|
|
|
my ($storePath, $url, $head) = @_;
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-07-06 06:30:40 +02:00
|
|
|
|
my $curl = WWW::Curl::Easy->new;
|
|
|
|
|
my $curlId = $curlIdCount++;
|
2013-06-07 15:33:44 +02:00
|
|
|
|
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET"
|
|
|
|
|
, shown => 0, started => time() };
|
2012-07-06 06:30:40 +02:00
|
|
|
|
|
|
|
|
|
$curl->setopt(CURLOPT_PRIVATE, $curlId);
|
|
|
|
|
$curl->setopt(CURLOPT_URL, $url);
|
2012-09-12 16:41:17 +02:00
|
|
|
|
open (my $fh, ">", \$requests{$curlId}->{content});
|
|
|
|
|
$curl->setopt(CURLOPT_WRITEDATA, $fh);
|
2012-07-06 06:30:40 +02:00
|
|
|
|
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
|
|
|
|
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
2014-12-09 13:15:31 +01:00
|
|
|
|
$curl->setopt(CURLOPT_SSL_VERIFYPEER, 0) unless isTrue($Nix::Config::config{"verify-https-binary-caches"} // "1");
|
2012-07-12 00:05:30 +02:00
|
|
|
|
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
|
2012-07-11 23:53:20 +02:00
|
|
|
|
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
2012-07-26 23:11:11 +02:00
|
|
|
|
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
2014-02-26 15:58:37 +01:00
|
|
|
|
$curl->setopt(CURLOPT_CONNECTTIMEOUT, $curlConnectTimeout);
|
2014-08-21 15:15:50 +02:00
|
|
|
|
$curl->setopt(CURLOPT_TIMEOUT, 20 * 60);
|
2012-07-06 06:30:40 +02: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-07 01:08:20 +02:00
|
|
|
|
if ($activeRequests >= $maxParallelRequests) {
|
|
|
|
|
$scheduled{$curlId} = 1;
|
|
|
|
|
} else {
|
|
|
|
|
$curlm->add_handle($curl);
|
|
|
|
|
$activeRequests++;
|
|
|
|
|
}
|
2012-07-06 06:30:40 +02: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-07 01:08:20 +02:00
|
|
|
|
return $requests{$curlId};
|
2012-07-06 06:30:40 +02: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) {
|
2013-06-07 15:33:44 +02:00
|
|
|
|
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 1.0);
|
2012-07-06 06:30:40 +02:00
|
|
|
|
}
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-07-06 06:30:40 +02:00
|
|
|
|
if ($curlm->perform() != $activeRequests) {
|
|
|
|
|
while (my ($id, $result) = $curlm->info_read) {
|
|
|
|
|
if ($id) {
|
2012-07-17 22:19:40 +02:00
|
|
|
|
my $request = $requests{$id} or die;
|
|
|
|
|
my $handle = $request->{handle};
|
|
|
|
|
$request->{result} = $result;
|
2012-07-26 23:11:11 +02:00
|
|
|
|
$request->{httpStatus} = $handle->getinfo(CURLINFO_RESPONSE_CODE);
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-07-17 22:19:40 +02:00
|
|
|
|
print STDERR "$request->{type} on $request->{url} [$request->{result}, $request->{httpStatus}]\n" if $debug;
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-07-06 06:30:40 +02:00
|
|
|
|
$activeRequests--;
|
2012-07-17 22:19:40 +02: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-07 01:08:20 +02:00
|
|
|
|
|
|
|
|
|
if (scalar(keys %scheduled) > 0) {
|
|
|
|
|
my $id2 = (keys %scheduled)[0];
|
|
|
|
|
$curlm->add_handle($requests{$id2}->{handle});
|
|
|
|
|
$activeRequests++;
|
|
|
|
|
delete $scheduled{$id2};
|
|
|
|
|
}
|
2012-07-06 06:30:40 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-07 15:33:44 +02:00
|
|
|
|
|
|
|
|
|
my $time = time();
|
|
|
|
|
while (my ($key, $request) = each %requests) {
|
|
|
|
|
next unless defined $request->{handle};
|
|
|
|
|
next if $request->{shown};
|
|
|
|
|
if ($time > $request->{started} + $showAfter) {
|
|
|
|
|
print STDERR "still waiting for ‘$request->{url}’ after $showAfter seconds...\n";
|
|
|
|
|
$request->{shown} = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-06 06:30:40 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-03 23:29:33 +02:00
|
|
|
|
|
|
|
|
|
sub initCache {
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
my $dbPath = "$Nix::Config::stateDir/binary-cache-v3.sqlite";
|
2012-11-06 17:45:20 +01:00
|
|
|
|
|
|
|
|
|
unlink "$Nix::Config::stateDir/binary-cache-v1.sqlite";
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
unlink "$Nix::Config::stateDir/binary-cache-v2.sqlite";
|
2012-07-03 23:29:33 +02:00
|
|
|
|
|
|
|
|
|
# Open/create the database.
|
|
|
|
|
$dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
|
2014-08-20 17:00:17 +02:00
|
|
|
|
or die "cannot open database ‘$dbPath’";
|
2012-07-03 23:29:33 +02:00
|
|
|
|
$dbh->{RaiseError} = 1;
|
|
|
|
|
$dbh->{PrintError} = 0;
|
|
|
|
|
|
2012-12-06 11:43:34 +01:00
|
|
|
|
$dbh->sqlite_busy_timeout(60 * 60 * 1000);
|
|
|
|
|
|
2012-07-03 23:29:33 +02:00
|
|
|
|
$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-28 00:16:05 +02:00
|
|
|
|
url text unique not null,
|
|
|
|
|
timestamp integer not null,
|
|
|
|
|
storeDir text not null,
|
2012-11-06 17:45:20 +01:00
|
|
|
|
wantMassQuery integer not null,
|
|
|
|
|
priority integer not null
|
2012-07-03 23:29:33 +02:00
|
|
|
|
);
|
|
|
|
|
EOF
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-07-03 23:29:33 +02: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,
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
signedBy text,
|
2012-07-03 23:29:33 +02:00
|
|
|
|
timestamp integer not null,
|
|
|
|
|
primary key (cache, storePath),
|
|
|
|
|
foreign key (cache) references BinaryCaches(id) on delete cascade
|
|
|
|
|
);
|
|
|
|
|
EOF
|
|
|
|
|
|
2012-07-04 00:54:46 +02:00
|
|
|
|
$dbh->do(<<EOF);
|
2012-07-11 23:53:20 +02:00
|
|
|
|
create table if not exists NARExistence (
|
2012-07-04 00:54:46 +02:00
|
|
|
|
cache integer not null,
|
|
|
|
|
storePath text not null,
|
2012-07-11 23:53:20 +02:00
|
|
|
|
exist integer not null,
|
2012-07-04 00:54:46 +02:00
|
|
|
|
timestamp integer not null,
|
|
|
|
|
primary key (cache, storePath),
|
|
|
|
|
foreign key (cache) references BinaryCaches(id) on delete cascade
|
|
|
|
|
);
|
|
|
|
|
EOF
|
|
|
|
|
|
2012-09-13 22:39:16 +02:00
|
|
|
|
$dbh->do("create index if not exists NARExistenceByExistTimestamp on NARExistence (exist, timestamp)");
|
|
|
|
|
|
2012-11-06 17:45:20 +01:00
|
|
|
|
$queryCache = $dbh->prepare("select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?") or die;
|
2012-07-28 00:16:05 +02:00
|
|
|
|
|
2012-07-03 23:29:33 +02:00
|
|
|
|
$insertNAR = $dbh->prepare(
|
|
|
|
|
"insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, " .
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
"narSize, refs, deriver, signedBy, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
2012-07-03 23:29:33 +02:00
|
|
|
|
|
|
|
|
|
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
|
2012-07-04 00:54:46 +02:00
|
|
|
|
|
2012-07-11 23:53:20 +02:00
|
|
|
|
$insertNARExistence = $dbh->prepare(
|
|
|
|
|
"insert or replace into NARExistence(cache, storePath, exist, timestamp) values (?, ?, ?, ?)") or die;
|
2012-07-04 00:54:46 +02:00
|
|
|
|
|
2012-09-13 22:39:16 +02:00
|
|
|
|
$queryNARExistence = $dbh->prepare("select exist, timestamp from NARExistence where cache = ? and storePath = ?") or die;
|
|
|
|
|
|
|
|
|
|
$expireNARExistence = $dbh->prepare("delete from NARExistence where exist = ? and timestamp < ?") or die;
|
2012-07-03 23:29:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-30 00:28:52 +02:00
|
|
|
|
|
2012-07-28 00:16:05 +02:00
|
|
|
|
sub getAvailableCaches {
|
|
|
|
|
return if $gotCaches;
|
|
|
|
|
$gotCaches = 1;
|
2012-07-11 23:53:20 +02:00
|
|
|
|
|
2012-08-01 00:56:22 +02:00
|
|
|
|
sub strToList {
|
|
|
|
|
my ($s) = @_;
|
|
|
|
|
return map { s/\/+$//; $_ } split(/ /, $s);
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-02 22:16:37 +01:00
|
|
|
|
my @urls = strToList($Nix::Config::config{"binary-caches"} //
|
2014-12-09 11:50:46 +01:00
|
|
|
|
($Nix::Config::storeDir eq "/nix/store" ? "https://cache.nixos.org" : ""));
|
2012-08-01 00:56:22 +02:00
|
|
|
|
|
2012-08-01 23:56:11 +02:00
|
|
|
|
my $urlsFiles = $Nix::Config::config{"binary-cache-files"}
|
2012-12-21 15:00:07 +01:00
|
|
|
|
// "$Nix::Config::stateDir/profiles/per-user/$userName/channels/binary-caches/*";
|
2012-08-01 23:56:11 +02:00
|
|
|
|
foreach my $urlFile (glob $urlsFiles) {
|
|
|
|
|
next unless -f $urlFile;
|
|
|
|
|
open FILE, "<$urlFile" or die "cannot open ‘$urlFile’\n";
|
|
|
|
|
my $url = <FILE>; chomp $url;
|
|
|
|
|
close FILE;
|
|
|
|
|
push @urls, strToList($url);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-07 15:37:28 +02:00
|
|
|
|
push @urls, strToList($Nix::Config::config{"extra-binary-caches"} // "");
|
|
|
|
|
|
2012-08-01 00:56:22 +02:00
|
|
|
|
# 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.
|
2013-05-07 15:37:28 +02:00
|
|
|
|
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
|
|
|
|
|
|
2012-08-01 00:56:22 +02:00
|
|
|
|
if (defined $Nix::Config::config{"untrusted-binary-caches"}) {
|
|
|
|
|
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"};
|
|
|
|
|
@urls = ();
|
|
|
|
|
foreach my $url (@untrustedUrls) {
|
2014-08-07 22:46:35 +02:00
|
|
|
|
die "binary cache ‘$url’ is not trusted (please add it to ‘trusted-binary-caches’ in $Nix::Config::confDir/nix.conf)\n"
|
2012-09-13 20:58:56 +02:00
|
|
|
|
unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
|
2012-08-01 17:19:24 +02:00
|
|
|
|
push @urls, $url;
|
2012-08-01 00:56:22 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-11 23:53:20 +02:00
|
|
|
|
|
2013-05-15 15:47:05 +02:00
|
|
|
|
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-extra-binary-caches"} // "";
|
2013-05-07 15:37:28 +02:00
|
|
|
|
foreach my $url (@untrustedUrls) {
|
2014-08-07 22:46:35 +02:00
|
|
|
|
unless (scalar(grep { $url eq $_ } @trustedUrls) > 0) {
|
|
|
|
|
warn "binary cache ‘$url’ is not trusted (please add it to ‘trusted-binary-caches’ in $Nix::Config::confDir/nix.conf)\n";
|
|
|
|
|
next;
|
|
|
|
|
}
|
2013-05-07 15:37:28 +02:00
|
|
|
|
push @urls, $url;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-17 22:45:04 +02:00
|
|
|
|
foreach my $url (uniq @urls) {
|
2012-07-11 23:53:20 +02:00
|
|
|
|
|
2012-07-28 00:16:05 +02:00
|
|
|
|
# FIXME: not atomic.
|
|
|
|
|
$queryCache->execute($url);
|
|
|
|
|
my $res = $queryCache->fetchrow_hashref();
|
|
|
|
|
if (defined $res) {
|
|
|
|
|
next if $res->{storeDir} ne $Nix::Config::storeDir;
|
2012-11-06 17:45:20 +01:00
|
|
|
|
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $res->{wantMassQuery}, priority => $res->{priority} };
|
2012-07-28 00:16:05 +02:00
|
|
|
|
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;
|
2012-11-06 17:45:20 +01:00
|
|
|
|
my $priority = 50;
|
2012-07-28 00:16:05 +02:00
|
|
|
|
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); }
|
2012-11-06 17:45:20 +01:00
|
|
|
|
elsif ($1 eq "Priority") { $priority = int($2); }
|
2012-07-28 00:16:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-05 16:17:06 +02:00
|
|
|
|
$dbh->do("insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
|
2012-11-06 17:45:20 +01:00
|
|
|
|
{}, $url, time(), $storeDir, $wantMassQuery, $priority);
|
2013-06-05 16:17:06 +02:00
|
|
|
|
$queryCache->execute($url);
|
|
|
|
|
$res = $queryCache->fetchrow_hashref() or die;
|
2012-07-28 00:16:05 +02:00
|
|
|
|
next if $storeDir ne $Nix::Config::storeDir;
|
2013-06-05 16:17:06 +02:00
|
|
|
|
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
|
2012-07-28 00:16:05 +02:00
|
|
|
|
}
|
2012-09-13 22:39:16 +02:00
|
|
|
|
|
2012-11-06 17:45:20 +01:00
|
|
|
|
@caches = sort { $a->{priority} <=> $b->{priority} } @caches;
|
|
|
|
|
|
2012-09-13 22:39:16 +02:00
|
|
|
|
expireNegative();
|
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-07 01:08:20 +02:00
|
|
|
|
}
|
2012-07-04 00:54:46 +02:00
|
|
|
|
|
|
|
|
|
|
2013-04-23 12:43:28 +02:00
|
|
|
|
sub shouldCache {
|
|
|
|
|
my ($url) = @_;
|
|
|
|
|
return $cacheFileURLs || $url !~ /^file:/;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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-07 01:08:20 +02:00
|
|
|
|
sub processNARInfo {
|
2012-07-28 00:16:05 +02:00
|
|
|
|
my ($storePath, $cache, $request) = @_;
|
2012-07-06 06:30:40 +02:00
|
|
|
|
|
2012-07-26 23:11:11 +02:00
|
|
|
|
if ($request->{result} != 0) {
|
2013-06-04 15:20:37 +02:00
|
|
|
|
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
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-07 01:08:20 +02:00
|
|
|
|
print STDERR "could not download ‘$request->{url}’ (" .
|
2012-07-06 06:30:40 +02:00
|
|
|
|
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
2012-07-04 00:54:46 +02:00
|
|
|
|
} else {
|
2012-07-28 00:16:05 +02:00
|
|
|
|
$insertNARExistence->execute($cache->{id}, basename($storePath), 0, time())
|
2013-04-23 12:43:28 +02:00
|
|
|
|
if shouldCache $request->{url};
|
2012-07-04 00:54:46 +02:00
|
|
|
|
}
|
2012-06-30 00:28:52 +02:00
|
|
|
|
return undef;
|
|
|
|
|
}
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
my $narInfo = parseNARInfo($storePath, $request->{content}, $requireSignedBinaryCaches, $request->{url});
|
2012-10-17 22:45:04 +02:00
|
|
|
|
return undef unless defined $narInfo;
|
2012-07-18 17:01:17 +02:00
|
|
|
|
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
die if $requireSignedBinaryCaches && !defined $narInfo->{signedBy};
|
|
|
|
|
|
2012-07-04 00:35:39 +02:00
|
|
|
|
# Cache the result.
|
|
|
|
|
$insertNAR->execute(
|
2012-10-17 22:45:04 +02:00
|
|
|
|
$cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression},
|
|
|
|
|
$narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize},
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{signedBy}, time())
|
2013-04-23 12:43:28 +02:00
|
|
|
|
if shouldCache $request->{url};
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-10-17 22:45:04 +02:00
|
|
|
|
return $narInfo;
|
2012-07-03 23:29:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-07-11 16:13:16 +02:00
|
|
|
|
sub getCachedInfoFrom {
|
2012-07-28 00:16:05 +02:00
|
|
|
|
my ($storePath, $cache) = @_;
|
2012-07-03 23:29:33 +02:00
|
|
|
|
|
2012-07-28 00:16:05 +02:00
|
|
|
|
$queryNAR->execute($cache->{id}, basename($storePath));
|
2012-07-03 23:29:33 +02:00
|
|
|
|
my $res = $queryNAR->fetchrow_hashref();
|
2012-07-04 00:35:39 +02:00
|
|
|
|
return undef unless defined $res;
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
# We may previously have cached this info when signature checking
|
|
|
|
|
# was disabled. In that case, ignore the cached info.
|
|
|
|
|
return undef if $requireSignedBinaryCaches && !defined $res->{signedBy};
|
|
|
|
|
|
2012-07-27 15:59:18 +02:00
|
|
|
|
return
|
2012-07-03 23:29:33 +02: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}
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
, signedBy => $res->{signedBy}
|
2012-07-03 23:29:33 +02:00
|
|
|
|
} if defined $res;
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 03:55:36 +02:00
|
|
|
|
|
2012-07-28 00:16:05 +02:00
|
|
|
|
sub negativeHit {
|
|
|
|
|
my ($storePath, $cache) = @_;
|
|
|
|
|
$queryNARExistence->execute($cache->{id}, basename($storePath));
|
|
|
|
|
my $res = $queryNARExistence->fetchrow_hashref();
|
2012-09-13 22:39:16 +02:00
|
|
|
|
return defined $res && $res->{exist} == 0 && time() - $res->{timestamp} < $ttlNegativeUse;
|
2012-07-28 00:16:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-09-13 22:39:16 +02:00
|
|
|
|
sub expireNegative {
|
|
|
|
|
return if $didExpiration;
|
|
|
|
|
$didExpiration = 1;
|
|
|
|
|
my $time = time();
|
|
|
|
|
# Round up to the next multiple of the TTL to ensure that we do
|
|
|
|
|
# expiration only once per time interval. E.g. if $ttlNegative ==
|
|
|
|
|
# 3600, we expire entries at most once per hour. This is
|
|
|
|
|
# presumably faster than expiring a few entries per request (and
|
|
|
|
|
# thus doing a transaction).
|
|
|
|
|
my $limit = (int($time / $ttlNegative) - 1) * $ttlNegative;
|
|
|
|
|
$expireNARExistence->execute($limit, 0);
|
|
|
|
|
print STDERR "expired ", $expireNARExistence->rows, " negative entries\n" if $debug;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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-07 01:08:20 +02: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-30 00:28:52 +02:00
|
|
|
|
|
|
|
|
|
|
2012-07-09 00:39:07 +02: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-07 01:08:20 +02: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-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
if (defined $info) {
|
|
|
|
|
printInfo($storePath, $info);
|
|
|
|
|
$found = 1;
|
|
|
|
|
last;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
push @left, $storePath if !$found;
|
2012-06-30 00:28:52 +02: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-07 01:08:20 +02:00
|
|
|
|
return if scalar @left == 0;
|
|
|
|
|
|
2012-07-28 00:16:05 +02:00
|
|
|
|
foreach my $cache (@caches) {
|
2012-07-04 00:35:39 +02: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-07 01:08:20 +02:00
|
|
|
|
my @left2;
|
|
|
|
|
%requests = ();
|
|
|
|
|
foreach my $storePath (@left) {
|
2012-07-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
push @left2, $storePath;
|
|
|
|
|
next;
|
|
|
|
|
}
|
2012-07-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
processRequests;
|
|
|
|
|
|
|
|
|
|
foreach my $request (values %requests) {
|
2012-07-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
if (defined $info) {
|
|
|
|
|
printInfo($request->{storePath}, $info);
|
|
|
|
|
} else {
|
|
|
|
|
push @left2, $request->{storePath};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@left = @left2;
|
|
|
|
|
}
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 03:55:36 +02:00
|
|
|
|
|
2012-07-11 23:53:20 +02:00
|
|
|
|
sub printSubstitutablePaths {
|
|
|
|
|
my @paths = @_;
|
|
|
|
|
|
|
|
|
|
# First look for paths that have cached info.
|
|
|
|
|
my @left;
|
|
|
|
|
foreach my $storePath (@paths) {
|
|
|
|
|
my $found = 0;
|
2012-07-28 00:16:05 +02:00
|
|
|
|
foreach my $cache (@caches) {
|
|
|
|
|
next unless $cache->{wantMassQuery};
|
|
|
|
|
if (positiveHit($storePath, $cache)) {
|
2012-07-11 23:53:20 +02: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-28 00:16:05 +02:00
|
|
|
|
foreach my $cache (@caches) {
|
|
|
|
|
next unless $cache->{wantMassQuery};
|
2012-07-11 23:53:20 +02:00
|
|
|
|
my @left2;
|
|
|
|
|
%requests = ();
|
|
|
|
|
foreach my $storePath (@left) {
|
2012-07-28 00:16:05 +02:00
|
|
|
|
if (negativeHit($storePath, $cache)) {
|
2012-07-11 23:53:20 +02:00
|
|
|
|
push @left2, $storePath;
|
|
|
|
|
next;
|
|
|
|
|
}
|
2012-07-28 00:16:05 +02:00
|
|
|
|
addRequest($storePath, infoUrl($cache->{url}, $storePath), 1);
|
2012-07-11 23:53:20 +02:00
|
|
|
|
}
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-07-11 23:53:20 +02:00
|
|
|
|
processRequests;
|
|
|
|
|
|
|
|
|
|
foreach my $request (values %requests) {
|
2012-07-26 23:11:11 +02:00
|
|
|
|
if ($request->{result} != 0) {
|
2013-06-04 15:20:37 +02:00
|
|
|
|
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
2012-07-11 23:53:20 +02:00
|
|
|
|
print STDERR "could not check ‘$request->{url}’ (" .
|
|
|
|
|
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
|
|
|
|
} else {
|
2012-07-28 00:16:05 +02:00
|
|
|
|
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time())
|
2013-04-23 12:43:28 +02:00
|
|
|
|
if shouldCache $request->{url};
|
2012-07-11 23:53:20 +02:00
|
|
|
|
}
|
|
|
|
|
push @left2, $request->{storePath};
|
|
|
|
|
} else {
|
2012-07-28 00:16:05 +02:00
|
|
|
|
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time())
|
2013-04-23 12:43:28 +02:00
|
|
|
|
if shouldCache $request->{url};
|
2012-07-11 23:53:20 +02:00
|
|
|
|
print "$request->{storePath}\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@left = @left2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-06-30 00:28:52 +02:00
|
|
|
|
sub downloadBinary {
|
2012-10-02 20:08:59 +02:00
|
|
|
|
my ($storePath, $destPath) = @_;
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-07-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
|
|
|
|
|
unless (defined $info) {
|
2012-07-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
processRequests;
|
2012-07-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next unless defined $info;
|
2012-07-27 15:59:18 +02: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-07 01:08:20 +02:00
|
|
|
|
my $decompressor;
|
2013-07-01 21:02:36 +02:00
|
|
|
|
if ($info->{compression} eq "bzip2") { $decompressor = "| $Nix::Config::bzip2 -d"; }
|
|
|
|
|
elsif ($info->{compression} eq "xz") { $decompressor = "| $Nix::Config::xz -d"; }
|
|
|
|
|
elsif ($info->{compression} eq "none") { $decompressor = ""; }
|
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-07 01:08:20 +02:00
|
|
|
|
else {
|
|
|
|
|
print STDERR "unknown compression method ‘$info->{compression}’\n";
|
|
|
|
|
next;
|
|
|
|
|
}
|
2012-07-28 00:16:05 +02:00
|
|
|
|
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
die if $requireSignedBinaryCaches && !defined $info->{signedBy};
|
|
|
|
|
print STDERR "\n*** Downloading ‘$url’ ", ($requireSignedBinaryCaches ? "(signed by ‘$info->{signedBy}’) " : ""), "to ‘$storePath’...\n";
|
2012-10-17 22:45:04 +02:00
|
|
|
|
checkURL $url;
|
2014-10-15 10:22:17 +02:00
|
|
|
|
if (system("$Nix::Config::curl --fail --location --insecure --connect-timeout $curlConnectTimeout '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
|
2014-08-20 17:00:17 +02:00
|
|
|
|
warn "download of ‘$url’ failed" . ($! ? ": $!" : "") . "\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-07 01:08:20 +02:00
|
|
|
|
next;
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
2012-07-27 18:16:02 +02:00
|
|
|
|
|
|
|
|
|
# Tell Nix about the expected hash so it can verify it.
|
Support cryptographically signed binary caches
NAR info files in binary caches can now have a cryptographic signature
that Nix will verify before using the corresponding NAR file.
To create a private/public key pair for signing and verifying a binary
cache, do:
$ openssl genrsa -out ./cache-key.sec 2048
$ openssl rsa -in ./cache-key.sec -pubout > ./cache-key.pub
You should also come up with a symbolic name for the key, such as
"cache.example.org-1". This will be used by clients to look up the
public key. (It's a good idea to number keys, in case you ever need
to revoke/replace one.)
To create a binary cache signed with the private key:
$ nix-push --dest /path/to/binary-cache --key ./cache-key.sec --key-name cache.example.org-1
The public key (cache-key.pub) should be distributed to the clients.
They should have a nix.conf should contain something like:
signed-binary-caches = *
binary-cache-public-key-cache.example.org-1 = /path/to/cache-key.pub
If all works well, then if Nix fetches something from the signed
binary cache, you will see a message like:
*** Downloading ‘http://cache.example.org/nar/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’ (signed by ‘cache.example.org-1’) to ‘/nix/store/7dppcj5sc1nda7l54rjc0g5l1hamj09j-subversion-1.7.11’...
On the other hand, if the signature is wrong, you get a message like
NAR info file `http://cache.example.org/7dppcj5sc1nda7l54rjc0g5l1hamj09j.narinfo' has an invalid signature; ignoring
Signatures are implemented as a single line appended to the NAR info
file, which looks like this:
Signature: 1;cache.example.org-1;HQ9Xzyanq9iV...muQ==
Thus the signature has 3 fields: a version (currently "1"), the ID of
key, and the base64-encoded signature of the SHA-256 hash of the
contents of the NAR info file up to but not including the Signature
line.
Issue #75.
2014-01-08 15:23:41 +01:00
|
|
|
|
die unless defined $info->{narHash} && $info->{narHash} ne "";
|
2012-07-27 18:16:02 +02:00
|
|
|
|
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-07 01:08:20 +02:00
|
|
|
|
print STDERR "\n";
|
2012-07-28 00:16:05 +02:00
|
|
|
|
return;
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-28 00:16:05 +02:00
|
|
|
|
print STDERR "could not download ‘$storePath’ from any binary cache\n";
|
2013-04-23 12:45:01 +02:00
|
|
|
|
exit 1;
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 03:55:36 +02:00
|
|
|
|
|
2013-06-20 11:55:15 +02:00
|
|
|
|
# Bail out right away if binary caches are disabled.
|
|
|
|
|
exit 0 if
|
|
|
|
|
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
|
|
|
|
|
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
|
|
|
|
|
print "\n";
|
|
|
|
|
flush STDOUT;
|
|
|
|
|
|
2012-07-03 23:29:33 +02:00
|
|
|
|
initCache();
|
|
|
|
|
|
|
|
|
|
|
2012-06-30 00:28:52 +02:00
|
|
|
|
if ($ARGV[0] eq "--query") {
|
|
|
|
|
|
|
|
|
|
while (<STDIN>) {
|
2012-07-28 00:16:05 +02: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-07 01:08:20 +02:00
|
|
|
|
chomp;
|
|
|
|
|
my ($cmd, @args) = split " ", $_;
|
2012-07-27 15:59:18 +02:00
|
|
|
|
|
2012-06-30 00:28:52 +02:00
|
|
|
|
if ($cmd eq "have") {
|
2012-09-13 22:39:16 +02:00
|
|
|
|
print STDERR "checking binary caches for existence of @args\n" if $debug;
|
2012-07-11 23:53:20 +02:00
|
|
|
|
printSubstitutablePaths(@args);
|
|
|
|
|
print "\n";
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elsif ($cmd eq "info") {
|
2012-09-13 22:39:16 +02:00
|
|
|
|
print STDERR "checking binary caches for info on @args\n" if $debug;
|
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-07 01:08:20 +02:00
|
|
|
|
printInfoParallel(@args);
|
|
|
|
|
print "\n";
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-20 17:00:17 +02:00
|
|
|
|
else { die "unknown command ‘$cmd’"; }
|
2012-06-30 00:28:52 +02:00
|
|
|
|
|
|
|
|
|
flush STDOUT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elsif ($ARGV[0] eq "--substitute") {
|
|
|
|
|
my $storePath = $ARGV[1] or die;
|
2012-10-02 20:08:59 +02:00
|
|
|
|
my $destPath = $ARGV[2] or die;
|
2012-07-28 00:16:05 +02:00
|
|
|
|
getAvailableCaches;
|
2012-10-02 20:08:59 +02:00
|
|
|
|
downloadBinary($storePath, $destPath);
|
2012-06-30 00:28:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
die;
|
|
|
|
|
}
|