Remove s3binarystore (moved to nix in d155d80)
[?]
May 11, 2016, 4:34 PM
MS676RZWKHV4QNY2IILGKVA4DYOKFMSUCP2WTTJT374R3B3U2D6QCDependencies
- [2]
FS3HUMVUFix Makefile.am - [3]
XO774HEKFix a boost format string abort - [4]
DXFMYCNWFix build - [5]
W4W4ZTNVSome more logging - [6]
2PQPKMG7S3BinaryCacheStore: Use disk cache - [7]
XLYHZUHTCache .narinfo lookups - [8]
24BMQDZAStart of single-process hydra-queue-runner - [9]
GH4S4AWMRename file - [10]
7LB6QBXYKeep track of the number of build steps that are being built - [11]
73YR46NJhydra-queue-runner: Write directly to a binary cache - [12]
QSBS2ISOS3BinaryCacheStore::isValidPath(): Do a GET instead of HEAD - [13]
V6H6BWMKSync with Nix - [14]
5AIYUMTBBasic remote building - [15]
AMXZL5ORUse US standard S3 region - [16]
SOB276BAKeep some statistics for the binary cache stores - [17]
EOO4EFWDUse a single BinaryCacheStore for all threads - [18]
N2NKSKHSRefactor local binary cache code into a subclass - [19]
DIEY5USNKeep better bytesReceived/bytesSent stats - [20]
YZAI5GQUImplement a database connection pool - [21]
B2L4T3X6Sync with Nix - [22]
ENXUSMSVMake concurrency more robust - [23]
MB3TISH2Rate-limit the number of threads copying closures at the same time - [24]
GTUZLZRHAdd an S3-backed binary cache store - [25]
N4IROACVMove buildRemote() into State - [26]
MHVIT4JYSplit hydra-queue-runner.cc more - [27]
QDEGRYZVReduce severity level of some message - [28]
RX6UB7YWBetter AWS error messages - [29]
ZM34T2NWTypo - [30]
HJOEIMLRRefactor - [31]
US3QQSHJUpdates for negative .narinfo caching
Change contents
- file deletion: s3-binary-cache-store.cc
#include "s3-binary-cache-store.hh"#include <aws/core/client/ClientConfiguration.h>#include <aws/s3/S3Client.h>#include <aws/s3/model/CreateBucketRequest.h>#include <aws/s3/model/GetBucketLocationRequest.h>#include <aws/s3/model/GetObjectRequest.h>#include <aws/s3/model/HeadObjectRequest.h>#include <aws/s3/model/PutObjectRequest.h>namespace nix {/* Helper: given an Outcome<R, E>, return R in case of success, orthrow an exception in case of an error. */template<typename R, typename E>R && checkAws(const FormatOrString & fs, Aws::Utils::Outcome<R, E> && outcome){if (!outcome.IsSuccess())throw S3Error(outcome.GetError().GetErrorType(),fs.s + ": " + outcome.GetError().GetMessage());return outcome.GetResultWithOwnership();}S3BinaryCacheStore::S3BinaryCacheStore(std::shared_ptr<Store> localStore,const Path & secretKeyFile, const std::string & bucketName): BinaryCacheStore(localStore, secretKeyFile), bucketName(bucketName), config(makeConfig()), client(make_ref<Aws::S3::S3Client>(*config)){diskCache = getNarInfoDiskCache();}std::string S3BinaryCacheStore::getUri(){return "s3://" + bucketName;}ref<Aws::Client::ClientConfiguration> S3BinaryCacheStore::makeConfig(){auto res = make_ref<Aws::Client::ClientConfiguration>();res->region = Aws::Region::US_EAST_1;res->requestTimeoutMs = 600 * 1000;return res;}void S3BinaryCacheStore::init(){if (!diskCache->cacheExists(getUri())) {/* Create the bucket if it doesn't already exists. */// FIXME: HeadBucket would be more appropriate, but doesn't return// an easily parsed 404 message.auto res = client->GetBucketLocation(Aws::S3::Model::GetBucketLocationRequest().WithBucket(bucketName));if (!res.IsSuccess()) {if (res.GetError().GetErrorType() != Aws::S3::S3Errors::NO_SUCH_BUCKET)throw Error(format("AWS error checking bucket ‘%s’: %s") % bucketName % res.GetError().GetMessage());checkAws(format("AWS error creating bucket ‘%s’") % bucketName,client->CreateBucket(Aws::S3::Model::CreateBucketRequest().WithBucket(bucketName).WithCreateBucketConfiguration(Aws::S3::Model::CreateBucketConfiguration()/* .WithLocationConstraint(Aws::S3::Model::BucketLocationConstraint::US) */ )));}diskCache->createCache(getUri());}BinaryCacheStore::init();}const S3BinaryCacheStore::Stats & S3BinaryCacheStore::getS3Stats(){return stats;}/* This is a specialisation of isValidPath() that optimisticallyfetches the .narinfo file, rather than first checking for itsexistence via a HEAD request. Since .narinfos are small, doing aGET is unlikely to be slower than HEAD. */bool S3BinaryCacheStore::isValidPathUncached(const Path & storePath){try {queryPathInfo(storePath);return true;} catch (InvalidPath & e) {return false;}}bool S3BinaryCacheStore::fileExists(const std::string & path){stats.head++;auto res = client->HeadObject(Aws::S3::Model::HeadObjectRequest().WithBucket(bucketName).WithKey(path));if (!res.IsSuccess()) {auto & error = res.GetError();if (error.GetErrorType() == Aws::S3::S3Errors::UNKNOWN // FIXME&& error.GetMessage().find("404") != std::string::npos)return false;throw Error(format("AWS error fetching ‘%s’: %s") % path % error.GetMessage());}return true;}void S3BinaryCacheStore::upsertFile(const std::string & path, const std::string & data){auto request =Aws::S3::Model::PutObjectRequest().WithBucket(bucketName).WithKey(path);auto stream = std::make_shared<std::stringstream>(data);request.SetBody(stream);stats.put++;stats.putBytes += data.size();auto now1 = std::chrono::steady_clock::now();auto result = checkAws(format("AWS error uploading ‘%s’") % path,client->PutObject(request));auto now2 = std::chrono::steady_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();printMsg(lvlInfo, format("uploaded ‘s3://%1%/%2%’ (%3% bytes) in %4% ms")% bucketName % path % data.size() % duration);stats.putTimeMs += duration;}std::shared_ptr<std::string> S3BinaryCacheStore::getFile(const std::string & path){auto request =Aws::S3::Model::GetObjectRequest().WithBucket(bucketName).WithKey(path);request.SetResponseStreamFactory([&]() {return Aws::New<std::stringstream>("STRINGSTREAM");});stats.get++;try {auto now1 = std::chrono::steady_clock::now();auto result = checkAws(format("AWS error fetching ‘%s’") % path,client->GetObject(request));auto now2 = std::chrono::steady_clock::now();auto res = dynamic_cast<std::stringstream &>(result.GetBody()).str();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count();printMsg(lvlTalkative, format("downloaded ‘s3://%1%/%2%’ (%3% bytes) in %4% ms")% bucketName % path % res.size() % duration);stats.getBytes += res.size();stats.getTimeMs += duration;return std::make_shared<std::string>(res);} catch (S3Error & e) {if (e.err == Aws::S3::S3Errors::NO_SUCH_KEY) return 0;throw;}}}printMsg(lvlDebug, format("fetching ‘s3://%1%/%2%’...") % bucketName % path);struct S3Error : public Error{Aws::S3::S3Errors err;S3Error(Aws::S3::S3Errors err, const FormatOrString & fs): Error(fs), err(err) { };};#include "nar-info.hh"#include "nar-info-disk-cache.hh" - file deletion: s3-binary-cache-store.hh
#pragma once#include "binary-cache-store.hh"namespace Aws { namespace Client { class ClientConfiguration; } }namespace Aws { namespace S3 { class S3Client; } }namespace nix {class S3BinaryCacheStore : public BinaryCacheStore{private:std::string bucketName;ref<Aws::Client::ClientConfiguration> config;ref<Aws::S3::S3Client> client;public:S3BinaryCacheStore(std::shared_ptr<Store> localStore,const Path & secretKeyFile, const std::string & bucketName);void init() override;private:ref<Aws::Client::ClientConfiguration> makeConfig();protected:bool fileExists(const std::string & path) override;void upsertFile(const std::string & path, const std::string & data) override;std::shared_ptr<std::string> getFile(const std::string & path) override;};}Stats stats;struct Stats{std::atomic<uint64_t> put{0};std::atomic<uint64_t> putBytes{0};std::atomic<uint64_t> putTimeMs{0};std::atomic<uint64_t> get{0};std::atomic<uint64_t> getBytes{0};std::atomic<uint64_t> getTimeMs{0};std::atomic<uint64_t> head{0};};const Stats & getS3Stats();bool isValidPathUncached(const Path & storePath) override;std::string getUri();#include <atomic> - edit in src/hydra-queue-runner/Makefile.am at line 6
s3-binary-cache-store.hh s3-binary-cache-store.cc \