diff --git a/CHANGELOG b/CHANGELOG index fd43f67e16..7fde08eda4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ devel ----- +* Fix execution ability in CentOS 6 regarding newer boost. + * Fixed permissions for dump/restore. * The _users collection is now properly restored when using arangorestore. diff --git a/CMakeLists.txt b/CMakeLists.txt index 384e0d386e..4b259ba417 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1140,6 +1140,7 @@ endif () # ZLIB_LIBS # ZLIB_INCLUDE_DIR +add_definitions(-DBOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX=1) #fix random provider add_definitions(-DBOOST_ALL_NO_LIB=1) #disable boost autolink on windows add_subdirectory(3rdParty) diff --git a/Documentation/DocuBlocks/Rest/Administration/get_admin_metrics.md b/Documentation/DocuBlocks/Rest/Administration/get_admin_metrics.md new file mode 100644 index 0000000000..e4a597ddb2 --- /dev/null +++ b/Documentation/DocuBlocks/Rest/Administration/get_admin_metrics.md @@ -0,0 +1,31 @@ + +@startDocuBlock get_admin_metrics +@brief return the current instance metrics + +@RESTHEADER{GET /_admin/metrics, Read the metrics, getMetrics} + +@RESTDESCRIPTION +Returns the instance's current metrics in Prometheus format. The +returned document collects all instance metrics, which are measured +at any given time and exposes them for collection by Prometheus. + +The document contains different metrics and metrics groups dependent +on the role of the queried instance. All exported metrics are +published with the `arangodb_` or `rocksdb_` string to distinguish +them from other collected data. + +The API then needs to be added to the Prometheus configuration file +for collection. + +@RESTRETURNCODES + +@RESTRETURNCODE{200} +Metrics were returned successfully. + +@RESTRETURNCODE{404} +The metrics API may be disabled using `--server.export-metrics-api +false` setting in the server. In this case, the result of the call +indicates the API to be not found. + +@endDocuBlock + diff --git a/arangod/ClusterEngine/ClusterTransactionState.cpp b/arangod/ClusterEngine/ClusterTransactionState.cpp index 02f7f6f970..8176b90df8 100644 --- a/arangod/ClusterEngine/ClusterTransactionState.cpp +++ b/arangod/ClusterEngine/ClusterTransactionState.cpp @@ -61,7 +61,7 @@ Result ClusterTransactionState::beginTransaction(transaction::Hints hints) { auto cleanup = scopeGuard([&] { if (nestingLevel() == 0) { updateStatus(transaction::Status::ABORTED); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsAborted++; } // free what we have got so far unuseCollections(nestingLevel()); @@ -75,7 +75,7 @@ Result ClusterTransactionState::beginTransaction(transaction::Hints hints) { // all valid if (nestingLevel() == 0) { updateStatus(transaction::Status::RUNNING); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsStarted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsStarted++; transaction::ManagerFeature::manager()->registerTransaction(id(), nullptr, isReadOnlyTransaction()); setRegistered(); @@ -122,7 +122,7 @@ Result ClusterTransactionState::commitTransaction(transaction::Methods* activeTr arangodb::Result res; if (nestingLevel() == 0) { updateStatus(transaction::Status::COMMITTED); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsCommitted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsCommitted++; } unuseCollections(nestingLevel()); @@ -136,7 +136,7 @@ Result ClusterTransactionState::abortTransaction(transaction::Methods* activeTrx Result res; if (nestingLevel() == 0) { updateStatus(transaction::Status::ABORTED); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsAborted++; } unuseCollections(nestingLevel()); diff --git a/arangod/MMFiles/MMFilesTransactionState.cpp b/arangod/MMFiles/MMFilesTransactionState.cpp index e831950227..ca87c25f5b 100644 --- a/arangod/MMFiles/MMFilesTransactionState.cpp +++ b/arangod/MMFiles/MMFilesTransactionState.cpp @@ -127,14 +127,14 @@ Result MMFilesTransactionState::beginTransaction(transaction::Hints hints) { // all valid if (nestingLevel() == 0) { updateStatus(transaction::Status::RUNNING); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsStarted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsStarted++; // defer writing of the begin marker until necessary! } } else { // something is wrong if (nestingLevel() == 0) { updateStatus(transaction::Status::ABORTED); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsAborted++; } // free what we have got so far @@ -175,7 +175,7 @@ Result MMFilesTransactionState::commitTransaction(transaction::Methods* activeTr } updateStatus(transaction::Status::COMMITTED); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsCommitted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsCommitted++; // if a write query, clear the query cache for the participating collections if (AccessMode::isWriteOrExclusive(_type) && !_collections.empty() && @@ -204,7 +204,8 @@ Result MMFilesTransactionState::abortTransaction(transaction::Methods* activeTrx result.reset(res); updateStatus(transaction::Status::ABORTED); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++; + _vocbase.server().getFeature(). + serverStatistics()._transactionsStatistics._transactionsAborted++; if (_hasOperations) { // must clean up the query cache because the transaction diff --git a/arangod/RestHandler/RestMetricsHandler.cpp b/arangod/RestHandler/RestMetricsHandler.cpp index ac3b884ec8..fc54dddb6b 100644 --- a/arangod/RestHandler/RestMetricsHandler.cpp +++ b/arangod/RestHandler/RestMetricsHandler.cpp @@ -58,6 +58,12 @@ RestStatus RestMetricsHandler::execute() { } MetricsFeature& metrics = server.getFeature(); + if (!metrics.exportAPI()) { + // dont export metrics, if so desired + generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); + return RestStatus::DONE; + } + std::string result; metrics.toPrometheus(result); _response->setResponseCode(rest::ResponseCode::OK); diff --git a/arangod/RestHandler/RestMetricsHandler.h b/arangod/RestHandler/RestMetricsHandler.h index d5452f07b8..b674b6b4fb 100644 --- a/arangod/RestHandler/RestMetricsHandler.h +++ b/arangod/RestHandler/RestMetricsHandler.h @@ -33,7 +33,7 @@ class RestMetricsHandler : public arangodb::RestBaseHandler { GeneralResponse*); char const* name() const override final { return "RestMetricsHandler"; } - RequestLane lane() const override final { return RequestLane::CLIENT_SLOW; } + RequestLane lane() const override final { return RequestLane::CLIENT_FAST; } RestStatus execute() override; }; diff --git a/arangod/RestServer/Metrics.cpp b/arangod/RestServer/Metrics.cpp index cc45b3baae..f878e907ab 100644 --- a/arangod/RestServer/Metrics.cpp +++ b/arangod/RestServer/Metrics.cpp @@ -66,6 +66,16 @@ Counter& Counter::operator++(int n) { return *this; } +Counter& Counter::operator+=(uint64_t const& n) { + count(n); + return *this; +} + +Counter& Counter::operator=(uint64_t const& n) { + store(n); + return *this; +} + void Counter::count() { ++_b; } @@ -84,6 +94,10 @@ uint64_t Counter::load() const { return _c.load(); } +void Counter::store(uint64_t const& n) { + _c.exchange(n); +} + void Counter::toPrometheus(std::string& result) const { _b.push(); result += "#TYPE " + name() + " counter\n"; @@ -91,7 +105,8 @@ void Counter::toPrometheus(std::string& result) const { result += name() + " " + std::to_string(load()) + "\n"; } -Counter::Counter(uint64_t const& val, std::string const& name, std::string const& help) : +Counter::Counter( + uint64_t const& val, std::string const& name, std::string const& help) : Metric(name, help), _c(val), _b(_c) {} Counter::~Counter() { _b.push(); } diff --git a/arangod/RestServer/Metrics.h b/arangod/RestServer/Metrics.h index 1881d264f4..02646dfcfe 100644 --- a/arangod/RestServer/Metrics.h +++ b/arangod/RestServer/Metrics.h @@ -45,7 +45,7 @@ template class Histogram; class Metric { public: Metric(std::string const& name, std::string const& help); - virtual ~Metric(); + virtual ~Metric(); std::string const& help() const; std::string const& name() const; virtual void toPrometheus(std::string& result) const = 0; @@ -62,7 +62,7 @@ struct Metrics { using counter_type = gcl::counter::simplex; using hist_type = gcl::counter::simplex_array; using buffer_type = gcl::counter::buffer; - + }; @@ -77,9 +77,12 @@ public: std::ostream& print (std::ostream&) const; Counter& operator++(); Counter& operator++(int); + Counter& operator+=(uint64_t const&); + Counter& operator=(uint64_t const&); void count(); void count(uint64_t); uint64_t load() const; + void store(uint64_t const&); void push(); virtual void toPrometheus(std::string&) const override; private: @@ -91,12 +94,12 @@ private: template class Gauge : public Metric { public: Gauge() = delete; - Gauge(uint64_t const& val, std::string const& name, std::string const& help) + Gauge(T const& val, std::string const& name, std::string const& help) : Metric(name, help) { _g.store(val); } Gauge(Gauge const&) = delete; - virtual ~Gauge(); + ~Gauge() = default; std::ostream& print (std::ostream&) const; Gauge& operator+=(T const& t) { _g.store(_g + t); @@ -111,6 +114,7 @@ public: return *this; } Gauge& operator/=(T const& t) { + TRI_ASSERT(t != T(0)); _g.store(_g / t); return *this; } @@ -121,7 +125,11 @@ public: T load() const { return _g.load(); }; - virtual void toPrometheus(std::string&) const override {}; + virtual void toPrometheus(std::string& result) const override { + result += "#TYPE " + name() + " gauge\n"; + result += "#HELP " + name() + " " + help() + "\n"; + result += name() + " " + std::to_string(load()) + "\n"; + }; private: std::atomic _g; }; @@ -130,41 +138,38 @@ std::ostream& operator<< (std::ostream&, Metrics::hist_type const&); /** * @brief Histogram functionality - */ + */ template class Histogram : public Metric { public: Histogram() = delete; - + Histogram (size_t const& buckets, T const& low, T const& high, std::string const& name, std::string const& help = "") : Metric(name, help), _c(Metrics::hist_type(buckets)), _low(low), _high(high), _lowr(std::numeric_limits::max()), _highr(std::numeric_limits::min()) { TRI_ASSERT(_c.size() > 0); _n = _c.size() - 1; - _div = std::floor((double)(high - low) / (double)_c.size()); + _div = (high - low) / (double)_c.size(); TRI_ASSERT(_div != 0); } - ~Histogram() {} - + ~Histogram() = default; + void records(T const& t) { if(t < _lowr) { _lowr = t; } else if (t > _highr) { _highr = t; - } - } - - void count(T const& t) { - if (t < _low) { - ++_c[0]; - } else if (t >= _high) { - ++_c[_n]; - } else { - ++_c[static_cast(std::floor(t / _div))]; } - records(t); + } + + size_t pos(T const& t) const { + return static_cast(std::floor((t - _low)/ _div)); + } + + void count(T const& t) { + count(t, 1); } void count(T const& t, uint64_t n) { @@ -173,7 +178,7 @@ public: } else if (t >= _high) { _c[_n] += n; } else { - _c[static_cast(std::floor(t / _div))] += n; + _c[pos(t)] += n; } records(t); } @@ -194,7 +199,7 @@ public: } uint64_t load(size_t i) const { return _c.load(i); }; - + size_t size() const { return _c.size(); } virtual void toPrometheus(std::string& result) const override { @@ -211,16 +216,17 @@ public: } result += name() + "_count " + std::to_string(sum) + "\n"; } - + std::ostream& print(std::ostream& o) const { - o << "_div: " << _div << ", _c: " << _c << ", _r: [" << _lowr << ", " << _highr << "] " << name(); + o << "_div: " << _div << ", _c: " << _c << ", _r: [" << _lowr << ", " << _highr << "] " << name(); return o; } +private: Metrics::hist_type _c; T _low, _high, _div, _lowr, _highr; size_t _n; - + }; diff --git a/arangod/RestServer/MetricsFeature.cpp b/arangod/RestServer/MetricsFeature.cpp index 187407ecda..dd211e0ad7 100644 --- a/arangod/RestServer/MetricsFeature.cpp +++ b/arangod/RestServer/MetricsFeature.cpp @@ -53,37 +53,28 @@ using namespace arangodb::options; // --SECTION-- MetricsFeature // ----------------------------------------------------------------------------- -MetricsFeature* MetricsFeature::METRICS = nullptr; - -#include MetricsFeature::MetricsFeature(application_features::ApplicationServer& server) - : ApplicationFeature(server, "Metrics"), - _enabled(true) { - METRICS = this; - _serverStatistics = new - ServerStatistics(std::chrono::duration( - std::chrono::system_clock::now().time_since_epoch()).count()); + : ApplicationFeature(server, "Metrics"), _export(true) { setOptional(false); startsAfter(); startsBefore(); } -void MetricsFeature::collectOptions(std::shared_ptr options) {} +void MetricsFeature::collectOptions(std::shared_ptr options) { + _serverStatistics = std::make_unique( + std::chrono::duration(std::chrono::system_clock::now().time_since_epoch()).count()); + options->addOption("--server.export-metrics-api", + "turn metrics API on or off", + new BooleanParameter(&_export), + arangodb::options::makeFlags(arangodb::options::Flags::Hidden)); +} + +bool MetricsFeature::exportAPI() const { + return _export; +} void MetricsFeature::validateOptions(std::shared_ptr) {} -void MetricsFeature::unprepare() { - METRICS = nullptr; -} - -void MetricsFeature::prepare() {} - -double time() { - return std::chrono::duration( // time since epoch in seconds - std::chrono::system_clock::now().time_since_epoch()) - .count(); -} - void MetricsFeature::toPrometheus(std::string& result) const { { std::lock_guard guard(_lock); @@ -111,38 +102,19 @@ void MetricsFeature::toPrometheus(std::string& result) const { Counter& MetricsFeature::counter ( std::string const& name, uint64_t const& val, std::string const& help) { - std::lock_guard guard(_lock); - auto const it = _registry.find(name); - if (it != _registry.end()) { - LOG_TOPIC("8523d", ERR, Logger::STATISTICS) << "Failed to retrieve histogram " << name; - TRI_ASSERT(false); - } - auto c = std::make_shared(val, name, help); - _registry.emplace(name, std::dynamic_pointer_cast(c)); - return *c; -}; -Counter& MetricsFeature::counter (std::string const& name) { - std::lock_guard guard(_lock); - auto it = _registry.find(name); - if (it == _registry.end()) { - LOG_TOPIC("32d58", ERR, Logger::STATISTICS) - << "Failed to retrieve counter " << name; - TRI_ASSERT(false); - throw std::exception(); - } - std::shared_ptr h; - try { - h = std::dynamic_pointer_cast(it->second); - } catch (std::exception const& e) { - LOG_TOPIC("853d2", ERR, Logger::STATISTICS) - << "Failed to retrieve counter " << name; - TRI_ASSERT(false); - throw(e); + auto metric = std::make_shared(val, name, help); + bool success = false; + { + std::lock_guard guard(_lock); + success = _registry.emplace(name, std::dynamic_pointer_cast(metric)).second; } - return *h; -}; - + if (!success) { + THROW_ARANGO_EXCEPTION_MESSAGE( + TRI_ERROR_INTERNAL, std::string("counter ") + name + " alredy exists"); + } + return *metric; +} ServerStatistics& MetricsFeature::serverStatistics() { return *_serverStatistics; diff --git a/arangod/RestServer/MetricsFeature.h b/arangod/RestServer/MetricsFeature.h index f2e06b52c9..1b0c7c3c17 100644 --- a/arangod/RestServer/MetricsFeature.h +++ b/arangod/RestServer/MetricsFeature.h @@ -34,121 +34,122 @@ namespace arangodb { class MetricsFeature final : public application_features::ApplicationFeature { public: - bool enabled() const { - return _enabled; - } + + using registry_type = std::unordered_map>; static double time(); explicit MetricsFeature(application_features::ApplicationServer& server); + bool exportAPI() const; + void collectOptions(std::shared_ptr) override final; void validateOptions(std::shared_ptr) override final; - void prepare() override final; - void unprepare() override final; + + template Histogram& histogram (std::string const& name, size_t const& buckets, T const& low, T const& high, std::string const& help = std::string()) { - - std::lock_guard guard(_lock); - auto const it = _registry.find(name); - if (it != _registry.end()) { - LOG_TOPIC("32e85", ERR, Logger::STATISTICS) << "histogram " << name << " alredy exists"; - TRI_ASSERT(false); + + auto metric = std::make_shared>(buckets, low, high, name, help); + bool success = false; + { + std::lock_guard guard(_lock); + success = _registry.try_emplace( + name, std::dynamic_pointer_cast(metric)).second; } - auto h = std::make_shared>(buckets, low, high, name, help); - _registry.emplace(name, std::dynamic_pointer_cast(h)); - return *h; + if (!success) { + THROW_ARANGO_EXCEPTION_MESSAGE( + TRI_ERROR_INTERNAL, std::string("histogram ") + name + " alredy exists"); + } + return *metric; + }; template Histogram& histogram (std::string const& name) { - std::lock_guard guard(_lock); - auto const it = _registry.find(name); - if (it == _registry.end()) { - LOG_TOPIC("32d85", ERR, Logger::STATISTICS) << "No histogram booked as " << name; - TRI_ASSERT(false); - throw std::exception(); - } - std::shared_ptr> h = nullptr; - try { - h = std::dynamic_pointer_cast>(*it->second); - if (h == nullptr) { - LOG_TOPIC("d2358", ERR, Logger::STATISTICS) << "Failed to retrieve histogram " << name; - } - } catch (std::exception const& e) { - LOG_TOPIC("32d75", ERR, Logger::STATISTICS) - << "Failed to retrieve histogram " << name << ": " << e.what(); - } - if (h == nullptr) { - TRI_ASSERT(false); - } - return *h; - }; + std::shared_ptr> metric = nullptr; + std::string error; + { + std::lock_guard guard(_lock); + registry_type::const_iterator it = _registry.find(name); + + try { + metric = std::dynamic_pointer_cast>(*it->second); + if (metric == nullptr) { + error = std::string("Failed to retrieve histogram ") + name; + } + } catch (std::exception const& e) { + error = std::string("Failed to retrieve histogram ") + name + ": " + e.what(); + } + } + if (!error.empty()) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, error); + } + return *metric; + + } Counter& counter(std::string const& name, uint64_t const& val, std::string const& help); - Counter& counter(std::string const& name); template Gauge& gauge(std::string const& name, T const& t, std::string const& help) { - std::lock_guard guard(_lock); - auto it = _registry.find(name); - if (it != _registry.end()) { - LOG_TOPIC("c7b37", ERR, Logger::STATISTICS) << "gauge " << name << " alredy exists"; - TRI_ASSERT(false); - throw(std::exception()); + auto metric = std::make_shared>(t, name, help); + bool success = false; + { + std::lock_guard guard(_lock); + success = _registry.try_emplace( + name, std::dynamic_pointer_cast(metric)).second; } - auto g = std::make_shared(t, name, help); - _registry.emplace(name, std::dynamic_pointer_cast(g)); - return *g; + if (!success) { + THROW_ARANGO_EXCEPTION_MESSAGE( + TRI_ERROR_INTERNAL, std::string("gauge ") + name + " alredy exists"); + } + return *metric; } template Gauge& gauge(std::string const& name) { - std::lock_guard guard(_lock); - auto it = _registry.find(name); - if (it == _registry.end()) { - LOG_TOPIC("5832d", ERR, Logger::STATISTICS) << "No metric booked as " << name; - TRI_ASSERT(false); - throw std::exception(); - } - std::shared_ptr> g = nullptr; - try { - g = std::dynamic_pointer_cast>(it->second); - if (g == nullptr) { - LOG_TOPIC("d4368", ERR, Logger::STATISTICS) << "Failed to retrieve gauge metric " << name; - TRI_ASSERT(false); - throw std::exception(); + + std::shared_ptr> metric = nullptr; + std::string error; + { + std::lock_guard guard(_lock); + registry_type::const_iterator it = _registry.find(name); + if (it == _registry.end()) { + THROW_ARANGO_EXCEPTION_MESSAGE( + TRI_ERROR_INTERNAL, std::string("No gauge booked as ") + name); + } + try { + metric = std::dynamic_pointer_cast>(*it->second); + if (metric == nullptr) { + error = std::string("Failed to retrieve gauge ") + name; + } + } catch (std::exception const& e) { + error = std::string("Failed to retrieve gauge ") + name + ": " + e.what(); } - } catch (std::exception const& e) { - LOG_TOPIC("c2348", ERR, Logger::STATISTICS) - << "Failed to retrieve gauge metric " << name << ": " << e.what(); - TRI_ASSERT(false); - throw(e); } + if (!error.empty()) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, error); + } + return *metric; } void toPrometheus(std::string& result) const; - static MetricsFeature* metrics() { - return METRICS; - } - ServerStatistics& serverStatistics(); - - private: - static MetricsFeature* METRICS; - bool _enabled; - - std::unordered_map> _registry; + private: + registry_type _registry; mutable std::mutex _lock; - ServerStatistics* _serverStatistics; - + std::unique_ptr _serverStatistics; + + bool _export; + }; } // namespace arangodb -#endif +#endif diff --git a/arangod/RestServer/arangod.cpp b/arangod/RestServer/arangod.cpp index db64272848..0780c9b75a 100644 --- a/arangod/RestServer/arangod.cpp +++ b/arangod/RestServer/arangod.cpp @@ -156,7 +156,6 @@ static int runServer(int argc, char** argv, ArangoGlobalContext& context) { std::type_index(typeid(GreetingsFeature)), std::type_index(typeid(HttpEndpointProvider)), std::type_index(typeid(LoggerBufferFeature)), - std::type_index(typeid(MetricsFeature)), std::type_index(typeid(pregel::PregelFeature)), std::type_index(typeid(ServerFeature)), std::type_index(typeid(SslServerFeature)), @@ -210,6 +209,7 @@ static int runServer(int argc, char** argv, ArangoGlobalContext& context) { server.addFeature(true); server.addFeature(); server.addFeature(); + server.addFeature(); server.addFeature(); server.addFeature(); server.addFeature(); @@ -230,7 +230,6 @@ static int runServer(int argc, char** argv, ArangoGlobalContext& context) { std::vector{std::type_index(typeid(ScriptFeature))}); server.addFeature(); server.addFeature(); - server.addFeature(); server.addFeature(); server.addFeature(); server.addFeature(name); diff --git a/arangod/RocksDBEngine/RocksDBTransactionState.cpp b/arangod/RocksDBEngine/RocksDBTransactionState.cpp index 4aedf9f4ab..be749e5b11 100644 --- a/arangod/RocksDBEngine/RocksDBTransactionState.cpp +++ b/arangod/RocksDBEngine/RocksDBTransactionState.cpp @@ -107,7 +107,7 @@ Result RocksDBTransactionState::beginTransaction(transaction::Hints hints) { // register with manager transaction::ManagerFeature::manager()->registerTransaction(id(), nullptr, isReadOnlyTransaction()); updateStatus(transaction::Status::RUNNING); - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsStarted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsStarted++; setRegistered(); @@ -398,7 +398,7 @@ Result RocksDBTransactionState::commitTransaction(transaction::Methods* activeTr if (res.ok()) { updateStatus(transaction::Status::COMMITTED); cleanupTransaction(); // deletes trx - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsCommitted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsCommitted++; } else { abortTransaction(activeTrx); // deletes trx } @@ -430,7 +430,7 @@ Result RocksDBTransactionState::abortTransaction(transaction::Methods* activeTrx TRI_ASSERT(!_rocksTransaction && !_cacheTx && !_readSnapshot); } - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._transactionsAborted++; unuseCollections(nestingLevel()); return result; } @@ -602,7 +602,7 @@ Result RocksDBTransactionState::triggerIntermediateCommit(bool& hasPerformedInte } hasPerformedIntermediateCommit = true; - MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._intermediateCommits++; + _vocbase.server().getFeature().serverStatistics()._transactionsStatistics._intermediateCommits++; TRI_IF_FAILURE("FailAfterIntermediateCommit") { THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG); diff --git a/arangod/Statistics/Descriptions.cpp b/arangod/Statistics/Descriptions.cpp index 6f1eebbeaa..84a636dd3b 100644 --- a/arangod/Statistics/Descriptions.cpp +++ b/arangod/Statistics/Descriptions.cpp @@ -17,7 +17,7 @@ /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// -/// @author Simon Grätzer +/// @author Simon Graetzer //////////////////////////////////////////////////////////////////////////////// #include "Descriptions.h" @@ -411,7 +411,10 @@ stats::Descriptions::Descriptions() } void stats::Descriptions::serverStatistics(velocypack::Builder& b) const { - ServerStatistics const& info = MetricsFeature::metrics()->serverStatistics(); + auto& server = application_features::ApplicationServer::server(); + + ServerStatistics const& info = + server.getFeature().serverStatistics(); b.add("uptime", VPackValue(info._uptime)); b.add("physicalMemory", VPackValue(TRI_PhysicalMemory)); @@ -422,7 +425,6 @@ void stats::Descriptions::serverStatistics(velocypack::Builder& b) const { b.add("intermediateCommits", VPackValue(info._transactionsStatistics._intermediateCommits.load())); b.close(); - auto& server = application_features::ApplicationServer::server(); V8DealerFeature& dealer = server.getFeature(); if (dealer.isEnabled()) { b.add("v8Context", VPackValue(VPackValueType::Object, true)); diff --git a/arangod/Statistics/ServerStatistics.cpp b/arangod/Statistics/ServerStatistics.cpp index e9107014c8..6eb5deb510 100644 --- a/arangod/Statistics/ServerStatistics.cpp +++ b/arangod/Statistics/ServerStatistics.cpp @@ -22,21 +22,23 @@ //////////////////////////////////////////////////////////////////////////////// #include "ServerStatistics.h" +#include "ApplicationFeatures/ApplicationFeature.h" #include "Statistics/StatisticsFeature.h" #include "RestServer/MetricsFeature.h" using namespace arangodb; TransactionStatistics::TransactionStatistics() : - _metrics(arangodb::MetricsFeature::metrics()), + _metrics(application_features::ApplicationServer::server(). + getFeature()), _transactionsStarted( - _metrics->counter("arangodb_transactions_started", 0, "Transactions started")), + _metrics.counter("arangodb_transactions_started", 0, "Transactions started")), _transactionsAborted( - _metrics->counter("arangodb_transactions_aborted", 0, "Transactions aborted")), + _metrics.counter("arangodb_transactions_aborted", 0, "Transactions aborted")), _transactionsCommitted( - _metrics->counter("arangodb_transactions_committed", 0, "Transactions committed")), + _metrics.counter("arangodb_transactions_committed", 0, "Transactions committed")), _intermediateCommits( - _metrics->counter("arangodb_intermediate_commits", 0, "Intermediate commits")) {} + _metrics.counter("arangodb_intermediate_commits", 0, "Intermediate commits")) {} // ----------------------------------------------------------------------------- // --SECTION-- static public methods diff --git a/arangod/Statistics/ServerStatistics.h b/arangod/Statistics/ServerStatistics.h index 53875b5f35..ec735fee83 100644 --- a/arangod/Statistics/ServerStatistics.h +++ b/arangod/Statistics/ServerStatistics.h @@ -40,7 +40,7 @@ struct TransactionStatistics { TransactionStatistics& operator=(TransactionStatistics const&) = delete; TransactionStatistics& operator=(TransactionStatistics &&) = delete; - arangodb::MetricsFeature* _metrics; + arangodb::MetricsFeature& _metrics; Counter& _transactionsStarted; Counter& _transactionsAborted; diff --git a/arangod/Statistics/StatisticsFeature.cpp b/arangod/Statistics/StatisticsFeature.cpp index 7c36d3d02a..cdbf67cd30 100644 --- a/arangod/Statistics/StatisticsFeature.cpp +++ b/arangod/Statistics/StatisticsFeature.cpp @@ -187,7 +187,8 @@ void StatisticsFeature::prepare() { STATISTICS = this; - MetricsFeature::metrics()->serverStatistics().initialize(StatisticsFeature::time()); + server().getFeature().serverStatistics().initialize( + StatisticsFeature::time()); ConnectionStatistics::initialize(); RequestStatistics::initialize(); } diff --git a/arangod/Statistics/StatisticsWorker.cpp b/arangod/Statistics/StatisticsWorker.cpp index 6786573b40..43c2578b67 100644 --- a/arangod/Statistics/StatisticsWorker.cpp +++ b/arangod/Statistics/StatisticsWorker.cpp @@ -1096,7 +1096,10 @@ void StatisticsWorker::generateRawStatistics(VPackBuilder& builder, double const RequestStatistics::fill(totalTime, requestTime, queueTime, ioTime, bytesSent, bytesReceived, stats::RequestStatisticsSource::ALL); - ServerStatistics const& serverInfo = MetricsFeature::metrics()->serverStatistics(); + + ServerStatistics const& serverInfo = + application_features::ApplicationServer::server(). + getFeature().serverStatistics(); builder.openObject(); if (!_clusterId.empty()) { diff --git a/arangod/V8Server/v8-statistics.cpp b/arangod/V8Server/v8-statistics.cpp index ed4c3dc916..0213201a6f 100644 --- a/arangod/V8Server/v8-statistics.cpp +++ b/arangod/V8Server/v8-statistics.cpp @@ -99,7 +99,9 @@ static void JS_ServerStatistics(v8::FunctionCallbackInfo const& args) TRI_V8_TRY_CATCH_BEGIN(isolate) v8::HandleScope scope(isolate); - ServerStatistics const& info = MetricsFeature::metrics()->serverStatistics(); + ServerStatistics const& info = + application_features::ApplicationServer::server(). + getFeature().serverStatistics(); v8::Handle result = v8::Object::New(isolate); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d49562a280..2581d1f7db 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -194,6 +194,7 @@ set(ARANGODB_TESTS_SOURCES Maintenance/MaintenanceRestHandlerTest.cpp Maintenance/MaintenanceTest.cpp ${RCFILE} + Metrics/MetricsTest.cpp Mocks/LogLevels.cpp Mocks/StorageEngineMock.cpp Mocks/Servers.cpp diff --git a/tests/Metrics/MetricsTest.cpp b/tests/Metrics/MetricsTest.cpp new file mode 100644 index 0000000000..2d234bd2c3 --- /dev/null +++ b/tests/Metrics/MetricsTest.cpp @@ -0,0 +1,171 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite for Cluster maintenance +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2017 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Kaveh Vahedipour +/// @author Matthew Von-Maszewski +/// @author Copyright 2017-2018, ArangoDB GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#include "gtest/gtest.h" + +#include "RestServer/Metrics.h" + +class MetricsTest : public ::testing::Test { +protected: + MetricsTest () {} +}; + + +TEST_F(MetricsTest, test_counter) { + + Counter c(0, "counter_1", "Counter 1"); + + ASSERT_EQ(c.load(), 0); + c++; + ASSERT_EQ(c.load(), 1); + c += 9; + ASSERT_EQ(c.load(), 10); + c = 0; + ASSERT_EQ(c.load(), 0); + + c.count(); + ASSERT_EQ(c.load(), 1); + c.count( 9); + ASSERT_EQ(c.load(), 10); + c.store(0); + ASSERT_EQ(c.load(), 0); + + std::string s; + c.toPrometheus(s); + LOG_DEVEL << s; + +} + +template void gauge_test() { + + T zdo = .1, zero = 0., one = 1.; + Gauge g(zero, "gauge_1", "Gauge 1"); + + ASSERT_DOUBLE_EQ(g.load(), zero); + g += zdo; + ASSERT_DOUBLE_EQ(g.load(), zdo); + g -= zdo; + ASSERT_DOUBLE_EQ(g.load(), zero); + g += zdo; + g *= g.load(); + ASSERT_DOUBLE_EQ(g.load(), zdo*zdo); + g /= g.load(); + ASSERT_DOUBLE_EQ(g.load(), one); + g -= g.load(); + ASSERT_DOUBLE_EQ(g.load(), zero); + + std::string s; + g.toPrometheus(s); + LOG_DEVEL << s; + +} + +TEST_F(MetricsTest, long_double) { + gauge_test(); +} + +TEST_F(MetricsTest, test_gauge_double) { + gauge_test(); +} + +TEST_F(MetricsTest, test_gauge_float) { + gauge_test(); +} + +template void histogram_test( + size_t const& buckets, T const& mn, T const& mx) { + + T span = mx - mn, step = span/(T)buckets, + mmin = (std::is_floating_point::value) ? span / T(1.e6) : T(1), + one = T(1), ten = T(10), thousand(10); + Histogram h(buckets, mn, mx, "hist_test", "Hist test"); + + //lower bucket bounds + for (size_t i = 0; i < buckets; ++i) { + auto d = mn + step*i + mmin; + h.count(d); + ASSERT_DOUBLE_EQ(h.load(i), 1); + } + + //upper bucket bounds + for (size_t i = 0; i < buckets; ++i) { + auto d = mn + step*(i+1) - mmin; + h.count(d); + ASSERT_DOUBLE_EQ(h.load(i), 2); + } + + //below lower limit + h.count(mn - one); + h.count(mn - ten); + h.count(mn - thousand); + ASSERT_DOUBLE_EQ(h.load(0), 5); + + // above upper limit + h.count(mx + one); + h.count(mx + ten); + h.count(mx + thousand); + ASSERT_DOUBLE_EQ(h.load(buckets-1), 5); + + // dump + std::string s; + h.toPrometheus(s); + LOG_DEVEL << s; + +} + + +TEST_F(MetricsTest, test_float_histogram) { + histogram_test( 9, 1., 2.); + histogram_test(10, -1., 1.); + histogram_test( 8, -2., -1.); +} +TEST_F(MetricsTest, test_double_histogram) { + histogram_test(12, 1., 2.); + histogram_test(11, -1., 1.); + histogram_test(13, -2., -1.); +} +TEST_F(MetricsTest, test_long_double_histogram) { + histogram_test(12, 1., 2.); + histogram_test(17, -1., 1.); + histogram_test( 7, -2., -1.); +} +TEST_F(MetricsTest, test_short_histogram) { + histogram_test(6, -17, 349); + histogram_test(7, 20, 40); + histogram_test(8, -63, -11); +} +TEST_F(MetricsTest, test_int_histogram) { + histogram_test(6, -17, 349); + histogram_test(7, 20, 40); + histogram_test(8, -63, -11); +} +TEST_F(MetricsTest, test_long_histogram) { + histogram_test(6, -17, 349); + histogram_test(7, 20, 40); + histogram_test(8, -63, -11); +} diff --git a/tests/js/client/server_permissions/test-hardened-rest-apis.js b/tests/js/client/server_permissions/test-hardened-rest-apis.js index f43141ac1d..bf912878be 100644 --- a/tests/js/client/server_permissions/test-hardened-rest-apis.js +++ b/tests/js/client/server_permissions/test-hardened-rest-apis.js @@ -115,6 +115,18 @@ function testSuite() { assertFalse(result.hasOwnProperty("foxxApi")); }, + testCanAccessAdminMetricsRw : function() { + arango.reconnect(endpoint, db._name(), "test_rw", "testi"); + let result = arango.GET("/_admin/metrics"); + }, + + testCanAccessAdminMetricsRo : function() { + arango.reconnect(endpoint, db._name(), "test_ro", "testi"); + let result = arango.GET("/_admin/metrics"); + assertTrue(result.error); + assertEqual(403, result.code); + }, + testCanAccessAdminLogRw : function() { arango.reconnect(endpoint, db._name(), "test_rw", "testi"); let result = arango.GET("/_admin/log"); diff --git a/tests/js/client/shell/shell-foxx-paths-spec-grey.js b/tests/js/client/shell/shell-foxx-paths-spec.js similarity index 97% rename from tests/js/client/shell/shell-foxx-paths-spec-grey.js rename to tests/js/client/shell/shell-foxx-paths-spec.js index 0eda024bd4..b7a89fab10 100644 --- a/tests/js/client/shell/shell-foxx-paths-spec-grey.js +++ b/tests/js/client/shell/shell-foxx-paths-spec.js @@ -18,17 +18,16 @@ describe('Foxx service path handling', () => { expect(encodeURIComponent('+')).not.to.equal('+'); expect(encodeURIComponent('/')).not.to.equal('/'); expect(encodeURIComponent(encodeURIComponent('/'))).not.to.equal(encodeURIComponent('/')); - }); - beforeEach(function () { + try { fm.uninstall(mount, {force: true}); } catch (e) {} fm.install(fs.join(basePath, 'paths'), mount); + internal.sleep(1); }); - afterEach(function () { - fm.uninstall(mount, {force: true}); - }); + beforeEach(function () { }); + afterEach(function () { }); it('supports plain URLs', () => { const res = request.get(`${baseUrl}/${mount}/hello/world`);