1
0
Fork 0

added query tracking feature

This commit is contained in:
Jan Steemann 2015-03-12 10:44:16 +01:00
parent 4545136e06
commit 64d77cbe24
12 changed files with 1060 additions and 18 deletions

View File

@ -34,6 +34,7 @@
#include "Aql/ExecutionPlan.h"
#include "Aql/Optimizer.h"
#include "Aql/Parser.h"
#include "Aql/QueryList.h"
#include "Basics/JsonHelper.h"
#include "Basics/json.h"
#include "Basics/tri-strings.h"
@ -53,6 +54,10 @@ using Json = triagens::basics::Json;
// --SECTION-- static const values
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief names of query phases / states
////////////////////////////////////////////////////////////////////////////////
static std::string StateNames[] = {
"initializing", // INITIALIZATION
"parsing", // PARSING
@ -75,9 +80,26 @@ static_assert(sizeof(StateNames) / sizeof(std::string) == static_cast<size_t>(Ex
/// @brief create a profile
////////////////////////////////////////////////////////////////////////////////
Profile::Profile ()
: results(static_cast<size_t>(INVALID_STATE)),
Profile::Profile (Query* query)
: query(query),
results(static_cast<size_t>(INVALID_STATE)),
stamp(TRI_microtime()) {
auto queryList = static_cast<QueryList*>(query->vocbase()->_queries);
try {
queryList->insert(query, stamp);
}
catch (...) {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy a profile
////////////////////////////////////////////////////////////////////////////////
Profile::~Profile () {
auto queryList = static_cast<QueryList*>(query->vocbase()->_queries);
queryList->remove(query, stamp);
}
////////////////////////////////////////////////////////////////////////////////
@ -89,7 +111,7 @@ void Profile::enter (ExecutionState state) {
if (state != ExecutionState::INVALID_STATE) {
// record duration of state
results.push_back(std::make_pair(state, now - stamp));
results.emplace_back(std::make_pair(state, now - stamp));
}
// set timestamp
@ -128,7 +150,8 @@ Query::Query (triagens::arango::ApplicationV8* applicationV8,
TRI_json_t* bindParameters,
TRI_json_t* options,
QueryPart part)
: _applicationV8(applicationV8),
: _id(TRI_NextQueryIdVocBase(vocbase)),
_applicationV8(applicationV8),
_vocbase(vocbase),
_executor(nullptr),
_context(nullptr),
@ -155,9 +178,7 @@ Query::Query (triagens::arango::ApplicationV8* applicationV8,
TRI_ASSERT(_vocbase != nullptr);
if (profiling()) {
_profile = new Profile;
}
_profile = new Profile(this);
enterState(INITIALIZATION);
_ast = new Ast(this);
@ -175,7 +196,8 @@ Query::Query (triagens::arango::ApplicationV8* applicationV8,
triagens::basics::Json queryStruct,
TRI_json_t* options,
QueryPart part)
: _applicationV8(applicationV8),
: _id(TRI_NextQueryIdVocBase(vocbase)),
_applicationV8(applicationV8),
_vocbase(vocbase),
_executor(nullptr),
_context(nullptr),
@ -202,9 +224,7 @@ Query::Query (triagens::arango::ApplicationV8* applicationV8,
TRI_ASSERT(_vocbase != nullptr);
if (profiling()) {
_profile = new Profile;
}
_profile = new Profile(this);
enterState(INITIALIZATION);
_ast = new Ast(this);
@ -619,7 +639,7 @@ QueryResult Query::execute (QueryRegistry* registry) {
result.json = jsonResult.steal();
result.stats = stats.steal();
if (_profile != nullptr) {
if (_profile != nullptr && profiling()) {
result.profile = _profile->toJson(TRI_UNKNOWN_MEM_ZONE);
}
@ -691,7 +711,7 @@ QueryResultV8 Query::executeV8 (v8::Isolate* isolate, QueryRegistry* registry) {
result.warnings = warningsToJson(TRI_UNKNOWN_MEM_ZONE);
result.stats = stats.steal();
if (_profile != nullptr) {
if (_profile != nullptr && profiling()) {
result.profile = _profile->toJson(TRI_UNKNOWN_MEM_ZONE);
}

View File

@ -38,6 +38,7 @@
#include "Aql/types.h"
#include "Utils/AqlTransaction.h"
#include "Utils/V8TransactionContext.h"
#include "VocBase/voc-types.h"
#include "V8Server/ApplicationV8.h"
struct TRI_json_t;
@ -57,6 +58,7 @@ namespace triagens {
class Executor;
class Expression;
class Parser;
class Query;
class QueryRegistry;
struct Variable;
@ -106,12 +108,18 @@ namespace triagens {
// -----------------------------------------------------------------------------
struct Profile {
Profile ();
Profile (Profile const&) = delete;
Profile& operator= (Profile const&) = delete;
explicit Profile (Query*);
~Profile ();
void enter (ExecutionState);
TRI_json_t* toJson (TRI_memory_zone_t*);
Query* query;
std::vector<std::pair<ExecutionState, double>> results;
double stamp;
};
@ -196,6 +204,14 @@ namespace triagens {
return _collections.collectionNames();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the query's id
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t id () const {
return _id;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the query string
////////////////////////////////////////////////////////////////////////////////
@ -478,6 +494,12 @@ namespace triagens {
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief query id
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t const _id;
////////////////////////////////////////////////////////////////////////////////
/// @brief application v8 used in the query, we need this for V8 context access
////////////////////////////////////////////////////////////////////////////////

279
arangod/Aql/QueryList.cpp Normal file
View File

@ -0,0 +1,279 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief Aql, list of running queries
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS 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 Jan Steemann
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "Aql/QueryList.h"
#include "Aql/Query.h"
#include "Basics/ReadLocker.h"
#include "Basics/WriteLocker.h"
#include "VocBase/vocbase.h"
using namespace triagens::aql;
// -----------------------------------------------------------------------------
// --SECTION-- struct QueryEntry
// -----------------------------------------------------------------------------
QueryEntry::QueryEntry (TRI_voc_tick_t id,
char const* queryString,
size_t queryLength,
double started)
: id(id),
queryString(queryString),
queryLength(queryLength),
started(started) {
}
// -----------------------------------------------------------------------------
// --SECTION-- struct QueryEntryCopy
// -----------------------------------------------------------------------------
QueryEntryCopy::QueryEntryCopy (TRI_voc_tick_t id,
std::string const& queryString,
double started,
double runTime)
: id(id),
queryString(queryString),
started(started),
runTime(runTime) {
}
// -----------------------------------------------------------------------------
// --SECTION-- class QueryList
// -----------------------------------------------------------------------------
double const QueryList::DefaultSlowQueryThreshold = 10.0;
size_t const QueryList::DefaultMaxSlowQueries = 64;
size_t const QueryList::DefaultMaxQueryStringLength = 4096;
// -----------------------------------------------------------------------------
// --SECTION-- constructors / destructors
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief create a query list
////////////////////////////////////////////////////////////////////////////////
QueryList::QueryList (TRI_vocbase_t* vocbase)
: _vocbase(vocbase),
_lock(),
_current(),
_slow(),
_slowCount(0),
_enabled(false),
_trackSlowQueries(true),
_slowQueryThreshold(QueryList::DefaultSlowQueryThreshold),
_maxSlowQueries(QueryList::DefaultMaxSlowQueries),
_maxQueryStringLength(QueryList::DefaultMaxQueryStringLength) {
_current.reserve(64);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy a query list
////////////////////////////////////////////////////////////////////////////////
QueryList::~QueryList () {
WRITE_LOCKER(_lock);
for (auto it : _current) {
delete it.second;
}
_current.clear();
_slow.clear();
}
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief insert a query
////////////////////////////////////////////////////////////////////////////////
void QueryList::insert (Query const* query,
double stamp) {
// no query string
if (query->queryString() == nullptr || ! _enabled) {
return;
}
try {
std::unique_ptr<QueryEntry> entry(new QueryEntry(
query->id(),
query->queryString(),
query->queryLength(),
stamp
));
WRITE_LOCKER(_lock);
auto it = _current.emplace(std::make_pair(query->id(), entry.get()));
if (it.second) {
entry.release();
}
}
catch (...) {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief remove a query
////////////////////////////////////////////////////////////////////////////////
void QueryList::remove (Query const* query,
double now) {
// no query string
if (query->queryString() == nullptr || ! _enabled) {
return;
}
QueryEntry* entry = nullptr;
{
WRITE_LOCKER(_lock);
auto it = _current.find(query->id());
if (it != _current.end()) {
entry = (*it).second;
_current.erase(it);
TRI_ASSERT(entry != nullptr);
try {
// check if we need to push the query into the list of slow queries
if (_slowQueryThreshold >= 0.0 &&
now - entry->started >= _slowQueryThreshold) {
// yes.
size_t const maxLength = _maxQueryStringLength;
size_t length = entry->queryLength;
if (length > maxLength) {
length = maxLength;
}
_slow.emplace_back(QueryEntryCopy(
entry->id,
std::string(entry->queryString, length).append(entry->queryLength > maxLength ? "..." : ""),
entry->started,
now - entry->started
));
if (++_slowCount > _maxSlowQueries) {
// free first element
_slow.pop_front();
--_slowCount;
}
}
}
catch (...) {
}
}
}
if (entry != nullptr) {
delete entry;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the list of currently running queries
////////////////////////////////////////////////////////////////////////////////
std::vector<QueryEntryCopy> QueryList::listCurrent () {
double const now = TRI_microtime();
std::vector<QueryEntryCopy> result;
{
size_t const maxLength = _maxQueryStringLength;
READ_LOCKER(_lock);
result.reserve(_current.size());
for (auto const& it : _current) {
auto entry = it.second;
if (entry == nullptr ||
entry->queryString == nullptr) {
continue;
}
size_t length = entry->queryLength;
if (length > maxLength) {
length = maxLength;
}
result.emplace_back(QueryEntryCopy(
entry->id,
std::string(entry->queryString, length).append(entry->queryLength > maxLength ? "..." : ""),
entry->started,
now - entry->started
));
}
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the list of slow queries
////////////////////////////////////////////////////////////////////////////////
std::vector<QueryEntryCopy> QueryList::listSlow () {
std::vector<QueryEntryCopy> result;
{
READ_LOCKER(_lock);
result.reserve(_slow.size());
result.assign(_slow.begin(), _slow.end());
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief clear the list of slow queries
////////////////////////////////////////////////////////////////////////////////
void QueryList::clearSlow () {
WRITE_LOCKER(_lock);
_slow.clear();
_slowCount = 0;
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:

331
arangod/Aql/QueryList.h Normal file
View File

@ -0,0 +1,331 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief Aql, list of queries running in database
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS 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 Jan Steemann
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_AQL_QUERY_LIST_H
#define ARANGODB_AQL_QUERY_LIST_H 1
#include "Basics/Common.h"
#include "Basics/ReadWriteLock.h"
#include "VocBase/voc-types.h"
struct TRI_vocbase_s;
namespace triagens {
namespace aql {
class Query;
// -----------------------------------------------------------------------------
// --SECTION-- struct QueryEntry
// -----------------------------------------------------------------------------
struct QueryEntry {
QueryEntry (TRI_voc_tick_t,
char const*,
size_t,
double);
TRI_voc_tick_t const id;
char const* const queryString;
size_t const queryLength;
double const started;
};
// -----------------------------------------------------------------------------
// --SECTION-- struct QueryEntryCopy
// -----------------------------------------------------------------------------
struct QueryEntryCopy {
QueryEntryCopy (TRI_voc_tick_t,
std::string const&,
double,
double);
TRI_voc_tick_t id;
std::string queryString;
double started;
double runTime;
};
// -----------------------------------------------------------------------------
// --SECTION-- class QueryList
// -----------------------------------------------------------------------------
class QueryList {
// -----------------------------------------------------------------------------
// --SECTION-- constructors / destructors
// -----------------------------------------------------------------------------
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief create a query list
////////////////////////////////////////////////////////////////////////////////
explicit QueryList (struct TRI_vocbase_s*);
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy a query list
////////////////////////////////////////////////////////////////////////////////
~QueryList ();
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not queries are tracked
////////////////////////////////////////////////////////////////////////////////
inline bool enabled () const {
return _enabled;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief toggle query tracking
////////////////////////////////////////////////////////////////////////////////
inline void enabled (bool value) {
_enabled = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not slow queries are tracked
////////////////////////////////////////////////////////////////////////////////
inline bool trackSlowQueries () const {
return _trackSlowQueries;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief toggle slow query tracking
////////////////////////////////////////////////////////////////////////////////
inline void trackSlowQueries (bool value) {
_trackSlowQueries = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief threshold for slow queries (in seconds)
////////////////////////////////////////////////////////////////////////////////
inline double slowQueryThreshold () const {
return _slowQueryThreshold;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the slow query threshold
////////////////////////////////////////////////////////////////////////////////
inline void slowQueryThreshold (double value) {
if (value < 0.0 || value == HUGE_VAL || value != value) {
// sanity checks
value = 0.0;
}
_slowQueryThreshold = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the max number of slow queries to keep
////////////////////////////////////////////////////////////////////////////////
inline size_t maxSlowQueries () const {
return _maxSlowQueries;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the max number of slow queries to keep
////////////////////////////////////////////////////////////////////////////////
inline void maxSlowQueries (size_t value) {
if (value > 16384) {
// sanity checks
value = 16384;
}
_maxSlowQueries = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the max length of query strings that are stored / returned
////////////////////////////////////////////////////////////////////////////////
inline size_t maxQueryStringLength () const {
return _maxQueryStringLength;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set the max length of query strings that are stored / returned
////////////////////////////////////////////////////////////////////////////////
inline void maxQueryStringLength (size_t value) {
// sanity checks
if (value < 64) {
value = 64;
}
else if (value >= 8 * 1024 * 1024) {
value = 8 * 1024 * 1024;
}
_maxQueryStringLength = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief enter a query
////////////////////////////////////////////////////////////////////////////////
void insert (Query const*,
double);
////////////////////////////////////////////////////////////////////////////////
/// @brief remove a query
////////////////////////////////////////////////////////////////////////////////
void remove (Query const*,
double);
////////////////////////////////////////////////////////////////////////////////
/// @brief return the list of running queries
////////////////////////////////////////////////////////////////////////////////
std::vector<QueryEntryCopy> listCurrent ();
////////////////////////////////////////////////////////////////////////////////
/// @brief return the list of slow queries
////////////////////////////////////////////////////////////////////////////////
std::vector<QueryEntryCopy> listSlow ();
////////////////////////////////////////////////////////////////////////////////
/// @brief clear the list of slow queries
////////////////////////////////////////////////////////////////////////////////
void clearSlow ();
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief vocbase
////////////////////////////////////////////////////////////////////////////////
struct TRI_vocbase_s* _vocbase;
////////////////////////////////////////////////////////////////////////////////
/// @brief r/w lock for the list
////////////////////////////////////////////////////////////////////////////////
triagens::basics::ReadWriteLock _lock;
////////////////////////////////////////////////////////////////////////////////
/// @brief list of current queries
////////////////////////////////////////////////////////////////////////////////
std::unordered_map<TRI_voc_tick_t, QueryEntry*> _current;
////////////////////////////////////////////////////////////////////////////////
/// @brief list of slow queries
////////////////////////////////////////////////////////////////////////////////
std::list<QueryEntryCopy> _slow;
////////////////////////////////////////////////////////////////////////////////
/// @brief current number of slow queries
////////////////////////////////////////////////////////////////////////////////
size_t _slowCount;
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not queries are tracked
////////////////////////////////////////////////////////////////////////////////
bool _enabled;
////////////////////////////////////////////////////////////////////////////////
/// @brief whether or not slow queries are tracked
////////////////////////////////////////////////////////////////////////////////
bool _trackSlowQueries;
////////////////////////////////////////////////////////////////////////////////
/// @brief threshold for slow queries (in seconds)
////////////////////////////////////////////////////////////////////////////////
double _slowQueryThreshold;
////////////////////////////////////////////////////////////////////////////////
/// @brief maximum number of slow queries to keep
////////////////////////////////////////////////////////////////////////////////
size_t _maxSlowQueries;
////////////////////////////////////////////////////////////////////////////////
/// @brief max length of query strings to return
////////////////////////////////////////////////////////////////////////////////
size_t _maxQueryStringLength;
////////////////////////////////////////////////////////////////////////////////
/// @brief default threshold for slow queries
////////////////////////////////////////////////////////////////////////////////
static double const DefaultSlowQueryThreshold;
////////////////////////////////////////////////////////////////////////////////
/// @brief default maximum number of slow queries to keep in list
////////////////////////////////////////////////////////////////////////////////
static size_t const DefaultMaxSlowQueries;
////////////////////////////////////////////////////////////////////////////////
/// @brief default max length of a query when returning it
////////////////////////////////////////////////////////////////////////////////
static size_t const DefaultMaxQueryStringLength;
};
}
}
#endif
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:

View File

@ -56,6 +56,7 @@ add_executable(
Aql/OptimizerRules.cpp
Aql/Parser.cpp
Aql/Query.cpp
Aql/QueryList.cpp
Aql/QueryRegistry.cpp
Aql/RangeInfo.cpp
Aql/Range.cpp

View File

@ -37,6 +37,7 @@ arangod_libarangod_a_SOURCES = \
arangod/Aql/OptimizerRules.cpp \
arangod/Aql/Parser.cpp \
arangod/Aql/Query.cpp \
arangod/Aql/QueryList.cpp \
arangod/Aql/QueryRegistry.cpp \
arangod/Aql/RangeInfo.cpp \
arangod/Aql/Range.cpp \

View File

@ -37,6 +37,7 @@
#include "v8-voccursor.h"
#include "Aql/Query.h"
#include "Aql/QueryList.h"
#include "Aql/QueryRegistry.h"
#include "Basics/Utf8Helper.h"
@ -107,7 +108,7 @@ int32_t const WRP_VOCBASE_COL_TYPE = 2;
// -----------------------------------------------------------------------------
// --SECTION-- HELPER FUNCTIONS
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief wraps a C++ into a v8::Object
////////////////////////////////////////////////////////////////////////////////
@ -1406,6 +1407,166 @@ static void JS_ExecuteAql (const v8::FunctionCallbackInfo<v8::Value>& args) {
TRI_WrapGeneralCursor(args, cursor);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief retrieve global query options or configure them
////////////////////////////////////////////////////////////////////////////////
static void JS_QueriesPropertiesAql (const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
TRI_vocbase_t* vocbase = GetContextVocBase(isolate);
if (vocbase == nullptr) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
}
auto queryList = static_cast<triagens::aql::QueryList*>(vocbase->_queries);
TRI_ASSERT(queryList != nullptr);
if (args.Length() > 1) {
TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_PROPERTIES(<options>)");
}
if (args.Length() == 1) {
// store options
if (! args[0]->IsObject()) {
TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_PROPERTIES(<options>)");
}
auto obj = args[0]->ToObject();
if (obj->Has(TRI_V8_ASCII_STRING("enabled"))) {
queryList->enabled(TRI_ObjectToBoolean(obj->Get(TRI_V8_ASCII_STRING("enabled"))));
}
if (obj->Has(TRI_V8_ASCII_STRING("trackSlowQueries"))) {
queryList->trackSlowQueries(TRI_ObjectToBoolean(obj->Get(TRI_V8_ASCII_STRING("trackSlowQueries"))));
}
if (obj->Has(TRI_V8_ASCII_STRING("maxSlowQueries"))) {
queryList->maxSlowQueries(static_cast<size_t>(TRI_ObjectToInt64(obj->Get(TRI_V8_ASCII_STRING("maxSlowQueries")))));
}
if (obj->Has(TRI_V8_ASCII_STRING("slowQueryThreshold"))) {
queryList->slowQueryThreshold(TRI_ObjectToDouble(obj->Get(TRI_V8_ASCII_STRING("slowQueryThreshold"))));
}
if (obj->Has(TRI_V8_ASCII_STRING("maxQueryStringLength"))) {
queryList->maxQueryStringLength(static_cast<size_t>(TRI_ObjectToInt64(obj->Get(TRI_V8_ASCII_STRING("maxQueryStringLength")))));
}
// fall-through intentional
}
// return current settings
auto result = v8::Object::New(isolate);
result->Set(TRI_V8_ASCII_STRING("enabled"), v8::Boolean::New(isolate, queryList->enabled()));
result->Set(TRI_V8_ASCII_STRING("trackSlowQueries"), v8::Boolean::New(isolate, queryList->trackSlowQueries()));
result->Set(TRI_V8_ASCII_STRING("maxSlowQueries"), v8::Number::New(isolate, static_cast<double>(queryList->maxSlowQueries())));
result->Set(TRI_V8_ASCII_STRING("slowQueryThreshold"), v8::Number::New(isolate, queryList->slowQueryThreshold()));
result->Set(TRI_V8_ASCII_STRING("maxQueryStringLength"), v8::Number::New(isolate, static_cast<double>(queryList->maxQueryStringLength())));
TRI_V8_RETURN(result);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the list of currently running queries
////////////////////////////////////////////////////////////////////////////////
static void JS_QueriesCurrentAql (const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
TRI_vocbase_t* vocbase = GetContextVocBase(isolate);
if (vocbase == nullptr) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
}
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_CURRENT()");
}
auto queryList = static_cast<triagens::aql::QueryList*>(vocbase->_queries);
TRI_ASSERT(queryList != nullptr);
try {
auto const&& queries = queryList->listCurrent();
uint32_t i = 0;
auto result = v8::Array::New(isolate, static_cast<int>(queries.size()));
for (auto it : queries) {
auto const&& timeString = TRI_StringTimeStamp(it.started);
v8::Handle<v8::Object> obj = v8::Object::New(isolate);
obj->Set(TRI_V8_ASCII_STRING("id"), V8TickId(isolate, it.id));
obj->Set(TRI_V8_ASCII_STRING("query"), TRI_V8_STD_STRING(it.queryString));
obj->Set(TRI_V8_ASCII_STRING("started"), TRI_V8_STD_STRING(timeString));
obj->Set(TRI_V8_ASCII_STRING("runTime"), v8::Number::New(isolate, it.runTime));
result->Set(i++, obj);
}
TRI_V8_RETURN(result);
}
catch (...) {
TRI_V8_THROW_EXCEPTION_MEMORY();
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the list of slow running queries or clears the list
////////////////////////////////////////////////////////////////////////////////
static void JS_QueriesSlowAql (const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
TRI_vocbase_t* vocbase = GetContextVocBase(isolate);
if (vocbase == nullptr) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
}
auto queryList = static_cast<triagens::aql::QueryList*>(vocbase->_queries);
TRI_ASSERT(queryList != nullptr);
if (args.Length() == 1) {
queryList->clearSlow();
TRI_V8_RETURN_TRUE();
}
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("AQL_QUERIES_SLOW()");
}
try {
auto const&& queries = queryList->listSlow();
uint32_t i = 0;
auto result = v8::Array::New(isolate, static_cast<int>(queries.size()));
for (auto it : queries) {
auto const&& timeString = TRI_StringTimeStamp(it.started);
v8::Handle<v8::Object> obj = v8::Object::New(isolate);
obj->Set(TRI_V8_ASCII_STRING("id"), V8TickId(isolate, it.id));
obj->Set(TRI_V8_ASCII_STRING("query"), TRI_V8_STD_STRING(it.queryString));
obj->Set(TRI_V8_ASCII_STRING("started"), TRI_V8_STD_STRING(timeString));
obj->Set(TRI_V8_ASCII_STRING("runTime"), v8::Number::New(isolate, it.runTime));
result->Set(i++, obj);
}
TRI_V8_RETURN(result);
}
catch (...) {
TRI_V8_THROW_EXCEPTION_MEMORY();
}
}
// -----------------------------------------------------------------------------
// --SECTION-- TRI_VOCBASE_T FUNCTIONS
// -----------------------------------------------------------------------------
@ -2678,6 +2839,9 @@ void TRI_InitV8VocBridge (v8::Isolate* isolate,
TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_EXPLAIN"), JS_ExplainAql, true);
TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_PARSE"), JS_ParseAql, true);
TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_WARNING"), JS_WarningAql, true);
TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERIES_PROPERTIES"), JS_QueriesPropertiesAql, true);
TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERIES_CURRENT"), JS_QueriesCurrentAql, true);
TRI_AddGlobalFunctionVocbase(isolate, context, TRI_V8_ASCII_STRING("AQL_QUERIES_SLOW"), JS_QueriesSlowAql, true);
TRI_InitV8replication(isolate, context, server, vocbase, loader, threadNumber, v8g);

View File

@ -35,6 +35,7 @@
#include <regex.h>
#include "Aql/QueryList.h"
#include "Basics/conversions.h"
#include "Basics/files.h"
#include "Basics/hashes.h"
@ -74,6 +75,8 @@
// --SECTION-- private types
// -----------------------------------------------------------------------------
static std::atomic<TRI_voc_tick_t> QueryId(1);
////////////////////////////////////////////////////////////////////////////////
/// @brief auxiliary struct for index iteration
////////////////////////////////////////////////////////////////////////////////
@ -1350,6 +1353,18 @@ TRI_vocbase_t* TRI_CreateInitialVocBase (TRI_server_t* server,
vocbase->_oldTransactions = nullptr;
try {
vocbase->_queries = new triagens::aql::QueryList(vocbase);
}
catch (...) {
TRI_Free(TRI_CORE_MEM_ZONE, vocbase->_name);
TRI_Free(TRI_CORE_MEM_ZONE, vocbase->_path);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, vocbase);
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
return nullptr;
}
// use the defaults provided
TRI_ApplyVocBaseDefaults(vocbase, defaults);
@ -1436,6 +1451,10 @@ void TRI_DestroyInitialVocBase (TRI_vocbase_t* vocbase) {
TRI_DestroySpin(&vocbase->_usage._lock);
TRI_FreeStoreGeneralCursor(vocbase->_cursors);
if (vocbase->_queries != nullptr) {
delete static_cast<triagens::aql::QueryList*>(vocbase->_queries);
}
// free name and path
TRI_Free(TRI_CORE_MEM_ZONE, vocbase->_path);
@ -2479,6 +2498,14 @@ bool TRI_IsAllowedNameVocBase (bool allowSystem,
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the next query id
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t TRI_NextQueryIdVocBase (TRI_vocbase_t* vocbase) {
return QueryId.fetch_add(1, std::memory_order_seq_cst);
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -311,6 +311,7 @@ typedef struct TRI_vocbase_s {
// structures for user-defined volatile data
void* _userStructures;
void* _queries;
TRI_associative_pointer_t _authInfo;
TRI_associative_pointer_t _authCache;
@ -648,6 +649,12 @@ bool TRI_IsSystemVocBase (TRI_vocbase_t*);
bool TRI_IsAllowedNameVocBase (bool,
char const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the next query id
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t TRI_NextQueryIdVocBase (TRI_vocbase_t*);
#endif
// -----------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
/*jshint strict: false */
/*global require, AQL_PARSE */
/*global require, AQL_PARSE, AQL_QUERIES_CURRENT, AQL_QUERIES_SLOW, AQL_QUERIES_PROPERTIES */
////////////////////////////////////////////////////////////////////////////////
/// @brief query actions
@ -34,9 +34,165 @@ var actions = require("org/arangodb/actions");
var ArangoError = arangodb.ArangoError;
// -----------------------------------------------------------------------------
// --SECTION-- global variables
// --SECTION-- HTTP methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock GetApiQueryCurrent
/// @brief returns a list of currently running AQL queries
///
/// @RESTHEADER{GET /_api/query/current, Returns the currently running AQL queries}
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200}
/// Is returned when the list of queries can be retrieved successfully.
///
/// @RESTRETURNCODE{400}
/// The server will respond with *HTTP 400* in case of a malformed request,
///
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock GetApiQuerySlow
/// @brief returns a list of slow running AQL queries
///
/// @RESTHEADER{GET /_api/query/slow, Returns the list of slow AQL queries}
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200}
/// Is returned when the list of queries can be retrieved successfully.
///
/// @RESTRETURNCODE{400}
/// The server will respond with *HTTP 400* in case of a malformed request,
///
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock GetApiQueryProperties
/// @brief returns the configuration for the AQL query tracking
///
/// @RESTHEADER{GET /_api/query/properties, Returns the properties for the AQL query tracking}
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200}
/// Is returned when the list of queries can be retrieved successfully.
///
/// @RESTRETURNCODE{400}
/// The server will respond with *HTTP 400* in case of a malformed request,
///
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
function get_api_query (req, res) {
var suffixes = [ "slow", "current", "properties" ];
if (req.suffix.length !== 1 ||
suffixes.indexOf(req.suffix[0]) === -1) {
actions.resultNotFound(req,
res,
arangodb.ERROR_HTTP_NOT_FOUND,
arangodb.errors.ERROR_HTTP_NOT_FOUND.message);
return;
}
var result;
if (req.suffix[0] === "slow") {
result = AQL_QUERIES_SLOW();
}
else if (req.suffix[0] === "current") {
result = AQL_QUERIES_CURRENT();
}
else if (req.suffix[0] === "properties") {
result = AQL_QUERIES_PROPERTIES();
}
if (result instanceof ArangoError) {
actions.resultBad(req, res, result.errorNum, result.errorMessage);
return;
}
actions.resultOk(req, res, actions.HTTP_OK, result);
}
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock DeleteApiQuerySlow
/// @brief clears the list of slow AQL queries
///
/// @RESTHEADER{DELETE /_api/query/slow, Clears the list of slow AQL queries}
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{204}
/// The server will respond with *HTTP 200* when the list of queries was
/// cleared successfully.
///
/// @RESTRETURNCODE{400}
/// The server will respond with *HTTP 400* in case of a malformed request.
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
function delete_api_query (req, res) {
if (req.suffix.length !== 1 || req.suffix[0] !== "slow") {
actions.resultNotFound(req,
res,
arangodb.ERROR_HTTP_NOT_FOUND,
arangodb.errors.ERROR_HTTP_NOT_FOUND.message);
return;
}
AQL_QUERIES_SLOW(true);
actions.resultOk(req, res, actions.HTTP_NO_CONTENT);
}
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock PutApiQueryProperties
/// @brief chages the configuration for the AQL query tracking
///
/// @RESTHEADER{PUT /_api/query/properties, Changes the properties for the AQL query tracking}
///
/// @RESTRETURNCODES
///
/// @RESTRETURNCODE{200}
/// Is returned if the properties were changed successfully.
///
/// @RESTRETURNCODE{400}
/// The server will respond with *HTTP 400* in case of a malformed request,
///
/// @endDocuBlock
////////////////////////////////////////////////////////////////////////////////
function put_api_query (req, res) {
if (req.suffix.length !== 1 ||
req.suffix[0] !== "properties") {
actions.resultNotFound(req,
res,
arangodb.ERROR_HTTP_NOT_FOUND,
arangodb.errors.ERROR_HTTP_NOT_FOUND.message);
return;
}
var json = actions.getJsonBody(req, res);
if (json === undefined) {
return;
}
var result = AQL_QUERIES_PROPERTIES(json);
if (result instanceof ArangoError) {
actions.resultBad(req, res, result.errorNum, result.errorMessage);
return;
}
actions.resultOk(req, res, actions.HTTP_OK, result);
}
////////////////////////////////////////////////////////////////////////////////
/// @startDocuBlock JSF_post_api_query
/// @brief parse an AQL query and return information about it
@ -144,10 +300,22 @@ actions.defineHttp({
callback : function (req, res) {
try {
switch (req.requestType) {
case actions.GET:
get_api_query(req, res);
break;
case actions.PUT:
put_api_query(req, res);
break;
case actions.POST:
post_api_query(req, res);
break;
case actions.DELETE:
delete_api_query(req, res);
break;
default:
actions.resultUnsupported(req, res);
}

View File

@ -904,6 +904,22 @@ char* TRI_StringUInt64Octal (uint64_t attr) {
return TRI_DuplicateString2(buffer, len);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief converts a time stamp to a string
////////////////////////////////////////////////////////////////////////////////
std::string TRI_StringTimeStamp (double stamp) {
char buffer[32];
size_t len;
struct tm tb;
time_t tt = static_cast<time_t>(stamp);
TRI_gmtime(tt, &tb);
len = strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", &tb);
return std::string(buffer, len);
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -318,6 +318,12 @@ char* TRI_StringUInt32Octal (uint32_t);
char* TRI_StringUInt64Octal (uint64_t);
////////////////////////////////////////////////////////////////////////////////
/// @brief converts a time stamp to a string
////////////////////////////////////////////////////////////////////////////////
std::string TRI_StringTimeStamp (double);
#endif
// -----------------------------------------------------------------------------