dynamically initialize mononoke backing stores

Summary: Move Mononoke backing store initialization process to every request so we can adjust the configuration value without restarting the process.

Reviewed By: strager

Differential Revision: D14583357

fbshipit-source-id: 49ce2736229ce3062d34337757ebda6bb6eae16a
This commit is contained in:
Zeyi Fan 2019-04-08 13:45:35 -07:00 committed by Facebook Github Bot
parent bb59fbd4cc
commit bbb0bc276e
2 changed files with 97 additions and 94 deletions

View File

@ -9,6 +9,7 @@
*/ */
#include "HgBackingStore.h" #include "HgBackingStore.h"
#include <folly/Synchronized.h>
#include <folly/ThreadLocal.h> #include <folly/ThreadLocal.h>
#include <folly/Try.h> #include <folly/Try.h>
#include <folly/executors/CPUThreadPoolExecutor.h> #include <folly/executors/CPUThreadPoolExecutor.h>
@ -28,6 +29,8 @@
#include "eden/fs/store/hg/HgImportPyError.h" #include "eden/fs/store/hg/HgImportPyError.h"
#include "eden/fs/store/hg/HgImporter.h" #include "eden/fs/store/hg/HgImporter.h"
#include "eden/fs/store/hg/HgProxyHash.h" #include "eden/fs/store/hg/HgProxyHash.h"
#include "eden/fs/store/mononoke/MononokeHttpBackingStore.h"
#include "eden/fs/utils/LazyInitialize.h"
#include "eden/fs/utils/SSLContext.h" #include "eden/fs/utils/SSLContext.h"
#include "eden/fs/utils/UnboundedQueueExecutor.h" #include "eden/fs/utils/UnboundedQueueExecutor.h"
@ -258,8 +261,7 @@ HgBackingStore::HgBackingStore(
HgImporter importer(repository, localStore); HgImporter importer(repository, localStore);
const auto& options = importer.getOptions(); const auto& options = importer.getOptions();
initializeTreeManifestImport(options, repository); initializeTreeManifestImport(options, repository);
repoName_ = options.repoName;
initializeMononoke(options);
#endif // EDEN_HAVE_HG_TREEMANIFEST #endif // EDEN_HAVE_HG_TREEMANIFEST
} }
@ -372,8 +374,8 @@ void HgBackingStore::initializeTreeManifestImport(
} }
#ifndef EDEN_WIN_NOMONONOKE #ifndef EDEN_WIN_NOMONONOKE
void HgBackingStore::initializeHttpMononokeBackingStore( std::unique_ptr<MononokeHttpBackingStore>
const ImporterOptions& options) { HgBackingStore::initializeHttpMononokeBackingStore() {
auto edenConfig = config_->getEdenConfig(); auto edenConfig = config_->getEdenConfig();
std::shared_ptr<folly::SSLContext> sslContext; std::shared_ptr<folly::SSLContext> sslContext;
@ -384,122 +386,106 @@ void HgBackingStore::initializeHttpMononokeBackingStore(
XLOG(WARN) << "mononoke is disabled because of build failure when " XLOG(WARN) << "mononoke is disabled because of build failure when "
"creating SSLContext: " "creating SSLContext: "
<< ex.what(); << ex.what();
return; return nullptr;
} }
auto executor = folly::getIOExecutor(); auto executor = folly::getIOExecutor();
auto hostName = edenConfig->getMononokeHostName(); auto hostName = edenConfig->getMononokeHostName();
if (hostName) { if (hostName) {
auto port = edenConfig->getMononokePort(); auto port = edenConfig->getMononokePort();
mononoke_ = std::make_unique<MononokeHttpBackingStore>( XLOG(DBG2) << "Initializing HTTP Mononoke backing store for repository "
<< repoName_ << ", using host " << *hostName << ":" << port;
return std::make_unique<MononokeHttpBackingStore>(
*hostName, *hostName,
folly::SocketAddress(hostName->c_str(), port, /*allowNameLookup=*/true), folly::SocketAddress(hostName->c_str(), port, /*allowNameLookup=*/true),
options.repoName, repoName_,
std::chrono::milliseconds(FLAGS_mononoke_timeout), std::chrono::milliseconds(FLAGS_mononoke_timeout),
executor.get(), executor.get(),
sslContext); sslContext);
XLOG(DBG2) << "HTTP Mononoke enabled for repository " << options.repoName
<< ", using host " << *hostName << ":" << port;
} else { } else {
const auto& tierName = edenConfig->getMononokeTierName(); const auto& tierName = edenConfig->getMononokeTierName();
mononoke_ = std::make_unique<MononokeHttpBackingStore>( XLOG(DBG2) << "Initializing HTTP Mononoke backing store for repository "
<< repoName_ << ", using tier " << tierName;
return std::make_unique<MononokeHttpBackingStore>(
tierName, tierName,
options.repoName, repoName_,
std::chrono::milliseconds(FLAGS_mononoke_timeout), std::chrono::milliseconds(FLAGS_mononoke_timeout),
executor.get(), executor.get(),
sslContext); sslContext);
XLOG(DBG2) << "HTTP Mononoke enabled for repository " << options.repoName
<< ", using tier " << tierName;
} }
} }
void HgBackingStore::initializeThriftMononokeBackingStore( std::unique_ptr<MononokeThriftBackingStore>
const ImporterOptions& options) { HgBackingStore::initializeThriftMononokeBackingStore() {
auto edenConfig = config_->getEdenConfig(); auto edenConfig = config_->getEdenConfig();
auto tierName = edenConfig->getMononokeTierName(); auto tierName = edenConfig->getMononokeTierName();
auto executor = folly::getIOExecutor(); auto executor = folly::getIOExecutor();
mononoke_ = std::make_unique<MononokeThriftBackingStore>(
tierName, options.repoName, executor);
XLOG(DBG2) << "Thrift Mononoke enabled for repository " << options.repoName XLOG(DBG2) << "Initializing thrift Mononoke backing store for repository "
<< ", using tier " << tierName; << repoName_ << ", using tier " << tierName;
return std::make_unique<MononokeThriftBackingStore>(
tierName, repoName_, executor);
} }
#endif #endif
#if defined(EDEN_HAVE_CURL) && EDEN_HAVE_HG_TREEMANIFEST #if defined(EDEN_HAVE_CURL) && EDEN_HAVE_HG_TREEMANIFEST
void HgBackingStore::initializeCurlMononokeBackingStore( std::unique_ptr<MononokeCurlBackingStore>
const ImporterOptions& options) { HgBackingStore::initializeCurlMononokeBackingStore() {
auto edenConfig = config_->getEdenConfig(); auto edenConfig = config_->getEdenConfig();
auto host = edenConfig->getMononokeHostName(); auto hostname = edenConfig->getMononokeHostName();
auto clientCertificate = edenConfig->getClientCertificate(); auto clientCertificate = edenConfig->getClientCertificate();
if (host == std::nullopt) { if (!hostname) {
XLOG(WARN) << "Mononoke is disabled because no Mononoke host is provided"; XLOG(WARN) << "Mononoke is disabled because no Mononoke host is provided";
return; return nullptr;
} }
if (clientCertificate == std::nullopt) { if (!clientCertificate) {
XLOG(WARN) XLOG(WARN)
<< "Mononoke is disabled because no client certificate is provided"; << "Mononoke is disabled because no client certificate is provided";
return; return nullptr;
} }
mononoke_ = std::make_unique<MononokeCurlBackingStore>( XLOG(DBG2) << "Initializing cURL Mononoke backing store for repository "
host.value(), << repoName_ << ", using host " << *hostname;
AbsolutePath(folly::to<std::string>(clientCertificate.value())),
options.repoName, return std::make_unique<MononokeCurlBackingStore>(
*hostname,
AbsolutePath(folly::to<std::string>(*clientCertificate)),
repoName_,
std::chrono::milliseconds(FLAGS_mononoke_timeout), std::chrono::milliseconds(FLAGS_mononoke_timeout),
folly::getCPUExecutor()); folly::getCPUExecutor());
XLOG(DBG2) << "cURL Mononoke enabled for repository " << options.repoName
<< ", using host " << *host;
} }
#endif #endif
void HgBackingStore::initializeMononoke(const ImporterOptions& options) { std::unique_ptr<BackingStore> HgBackingStore::initializeMononoke() {
if (!config_) {
XLOG(DBG2)
<< "mononoke is disabled because no config instance was provided to HgBackingStore";
return;
}
auto edenConfig = config_->getEdenConfig();
#if EDEN_HAVE_HG_TREEMANIFEST #if EDEN_HAVE_HG_TREEMANIFEST
if (options.repoName.empty()) { const auto& connectionType =
XLOG(DBG2) << "mononoke is disabled because it is not supported."; config_->getEdenConfig()->getMononokeConnectionType();
return;
}
auto useMononoke = edenConfig->getUseMononoke();
if (!useMononoke) {
XLOG(DBG2) << "mononoke is disabled by config.";
return;
}
const auto& connectionType = edenConfig->getMononokeConnectionType();
#ifndef EDEN_WIN_NOMONONOKE #ifndef EDEN_WIN_NOMONONOKE
if (connectionType == "http") { if (connectionType == "http") {
initializeHttpMononokeBackingStore(options); return initializeHttpMononokeBackingStore();
} else if (connectionType == "thrift") { } else if (connectionType == "thrift") {
initializeThriftMononokeBackingStore(options); return initializeThriftMononokeBackingStore();
} else if (connectionType == "curl") { } else if (connectionType == "curl") {
#ifdef EDEN_HAVE_CURL #ifdef EDEN_HAVE_CURL
initializeCurlMononokeBackingStore(options); return initializeCurlMononokeBackingStore();
#else #else // EDEN_HAVE_CURL
XLOG(WARN) XLOG(WARN)
<< "User specified Mononoke connection type as cURL, but eden is built without cURL"; << "User specified Mononoke connection type as cURL, but eden is built "
#endif "without cURL";
#endif // EDEN_HAVE_CURL
} else { } else {
XLOG(WARN) << "got unexpected value for `mononoke:connection-type`: " XLOG(WARN) << "got unexpected value for `mononoke:connection-type`: "
<< connectionType; << connectionType;
} }
#elif defined(EDEN_HAVE_CURL) #elif defined(EDEN_HAVE_CURL) // EDEN_WIN_NOMONONOKE
initializeCurlMononokeBackingStore(options); return initializeCurlMononokeBackingStore();
#endif #endif // EDEN_WIN_NOMONONOKE
#endif // EDEN_HAVE_HG_TREEMANIFEST #endif // EDEN_HAVE_HG_TREEMANIFEST
return nullptr;
} }
Future<unique_ptr<Tree>> HgBackingStore::getTree(const Hash& id) { Future<unique_ptr<Tree>> HgBackingStore::getTree(const Hash& id) {
@ -544,7 +530,8 @@ Future<unique_ptr<Tree>> HgBackingStore::importTreeImpl(
return tree; return tree;
} }
if (useMononoke()) { auto mononoke = getMononoke();
if (mononoke) {
// ask Mononoke API Server first because it has more metadata available // ask Mononoke API Server first because it has more metadata available
// than we'd get from a local treepack. Getting that data from mononoke // than we'd get from a local treepack. Getting that data from mononoke
// can save us from materializing so many file contents later to compute // can save us from materializing so many file contents later to compute
@ -552,7 +539,7 @@ Future<unique_ptr<Tree>> HgBackingStore::importTreeImpl(
XLOG(DBG4) << "importing tree \"" << manifestNode << "\" from mononoke"; XLOG(DBG4) << "importing tree \"" << manifestNode << "\" from mononoke";
RelativePath ownedPath(path); RelativePath ownedPath(path);
return mononoke_->getTree(manifestNode) return mononoke->getTree(manifestNode)
.via(serverThreadPool_) .via(serverThreadPool_)
.thenTry([edenTreeID, ownedPath, writeBatch]( .thenTry([edenTreeID, ownedPath, writeBatch](
auto mononokeTreeTry) mutable { auto mononokeTreeTry) mutable {
@ -625,18 +612,17 @@ HgBackingStore::fetchTreeFromHgCacheOrImporter(
} }
} }
bool HgBackingStore::useMononoke() const { std::shared_ptr<BackingStore> HgBackingStore::getMononoke() {
// Currently, useMononoke needs to be true at construction time in order // config_ might be uninitialized (e.g. testing).
// to configure the mononoke client safely. If that wasn't the case then if (!config_ || repoName_.empty()) {
// we'll treat it as disabled even if the user has subsequently configured return nullptr;
// it correctly.
if (!mononoke_) {
return false;
} }
DCHECK(config_) << "mononoke_ cannot be set without config_";
// Check to see if the user has disabled mononoke since starting the server. // Check to see if the user has disabled mononoke since starting the server.
return config_->getEdenConfig()->getUseMononoke(); auto useMononoke = config_->getEdenConfig()->getUseMononoke();
return lazyInitialize<BackingStore>(
useMononoke, mononoke_, [this]() { return initializeMononoke(); });
} }
folly::Future<std::unique_ptr<Tree>> HgBackingStore::fetchTreeFromImporter( folly::Future<std::unique_ptr<Tree>> HgBackingStore::fetchTreeFromImporter(
@ -827,11 +813,12 @@ Future<unique_ptr<Blob>> HgBackingStore::getBlob(const Hash& id) {
} }
} }
if (useMononoke()) { auto mononoke = getMononoke();
if (mononoke) {
XLOG(DBG5) << "requesting file contents of '" << hgInfo.path() << "', " XLOG(DBG5) << "requesting file contents of '" << hgInfo.path() << "', "
<< hgInfo.revHash().toString() << " from mononoke"; << hgInfo.revHash().toString() << " from mononoke";
auto revHashCopy = hgInfo.revHash(); auto revHashCopy = hgInfo.revHash();
return mononoke_->getBlob(revHashCopy) return mononoke->getBlob(revHashCopy)
.thenError([this, .thenError([this,
id, id,
path = hgInfo.path().copy(), path = hgInfo.path().copy(),

View File

@ -36,6 +36,9 @@ namespace eden {
class Importer; class Importer;
class ImporterOptions; class ImporterOptions;
class LocalStore; class LocalStore;
class MononokeHttpBackingStore;
class MononokeThriftBackingStore;
class MononokeCurlBackingStore;
class UnboundedQueueExecutor; class UnboundedQueueExecutor;
class ReloadableConfig; class ReloadableConfig;
@ -97,37 +100,49 @@ class HgBackingStore : public BackingStore {
AbsolutePathPiece repoPath); AbsolutePathPiece repoPath);
/** /**
* Initialize the mononoke_ needed for Mononoke API Server support. * Create a Mononoke backing store based on config_.
* *
* This leaves mononoke_ null if mononoke does not support the repository. * Return nullptr if something is wrong (e.g. missing configs).
*/ */
void initializeMononoke(const ImporterOptions& options); std::unique_ptr<BackingStore> initializeMononoke();
/**
* Get an instace of Mononoke backing store as specified in config_. This will
* call `initializeMononoke` if no active Mononoke instance is stored.
*
* Return nullptr if Mononoke is disabled.
*/
std::shared_ptr<BackingStore> getMononoke();
#ifndef EDEN_WIN_NOMONONOKE #ifndef EDEN_WIN_NOMONONOKE
/** /**
* Initialize the mononoke_ with MononokeHttpBackingStore, which uses * Create an instance of MononokeHttpBackingStore with values from config_
* HTTP to talk with Mononoke API Server. * (Proxygen based Mononoke client)
* *
* This leaves mononoke_ null if SSLContext cannot be constructed. * Return null if SSLContext cannot be constructed.
*/ */
void initializeHttpMononokeBackingStore(const ImporterOptions& options); std::unique_ptr<MononokeHttpBackingStore>
initializeHttpMononokeBackingStore();
/** /**
* Initialize the mononoke_ with MononokeThriftBackingStore, which uses * Create an instance of MononokeThriftBackingStore with values from config_
* thrift protocol to talk with Mononoke API Server. * (Thrift based Mononoke client)
*
* Return nullptr if required config is missing.
*/ */
void initializeThriftMononokeBackingStore(const ImporterOptions& options); std::unique_ptr<MononokeThriftBackingStore>
initializeThriftMononokeBackingStore();
#endif #endif
/** Returns true if we should use mononoke for a fetch */
bool useMononoke() const;
#if defined(EDEN_HAVE_CURL) && EDEN_HAVE_HG_TREEMANIFEST #if defined(EDEN_HAVE_CURL) && EDEN_HAVE_HG_TREEMANIFEST
/** /**
* Initialize the mononoke_ with MononokeCurlBackingStore, that is available * Create an instance of MononokeCurlBackingStore with values from config_
* on macOS * (Curl based Mononoke client)
*
* Return nullptr if required config is missing.
*/ */
void initializeCurlMononokeBackingStore(const ImporterOptions& options); std::unique_ptr<MononokeCurlBackingStore>
initializeCurlMononokeBackingStore();
#endif #endif
folly::Future<std::unique_ptr<Tree>> getTreeForCommitImpl(Hash commitID); folly::Future<std::unique_ptr<Tree>> getTreeForCommitImpl(Hash commitID);
@ -182,7 +197,8 @@ class HgBackingStore : public BackingStore {
std::unique_ptr<folly::Synchronized<UnionDatapackStore>> unionStore_; std::unique_ptr<folly::Synchronized<UnionDatapackStore>> unionStore_;
bool useDatapackGetBlob_{false}; bool useDatapackGetBlob_{false};
std::unique_ptr<BackingStore> mononoke_; std::string repoName_;
folly::Synchronized<std::shared_ptr<BackingStore>> mononoke_;
#ifndef EDEN_WIN_NO_RUST_DATAPACK #ifndef EDEN_WIN_NO_RUST_DATAPACK
std::optional<folly::Synchronized<DataPackUnion>> dataPackStore_; std::optional<folly::Synchronized<DataPackUnion>> dataPackStore_;
#endif #endif