From 42b7db66d12d001c1219569161921421261b0498 Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Tue, 8 Aug 2023 21:56:53 +0000 Subject: [PATCH 01/12] The minor extension made to the MySQL connector Also added the utility class to handle simple query cases. --- src/mysql/CMakeLists.txt | 1 + src/mysql/MySqlConnection.cc | 13 +++++ src/mysql/MySqlConnection.h | 2 + src/mysql/MySqlUtils.cc | 95 ++++++++++++++++++++++++++++++++++++ src/mysql/MySqlUtils.h | 71 +++++++++++++++++++++++++++ 5 files changed, 182 insertions(+) create mode 100644 src/mysql/MySqlUtils.cc create mode 100644 src/mysql/MySqlUtils.h diff --git a/src/mysql/CMakeLists.txt b/src/mysql/CMakeLists.txt index b83d3d6606..483eb8c3f7 100644 --- a/src/mysql/CMakeLists.txt +++ b/src/mysql/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(mysql PRIVATE LocalInfile.cc MySqlConfig.cc MySqlConnection.cc + MySqlUtils.cc RowBuffer.cc SchemaFactory.cc ) diff --git a/src/mysql/MySqlConnection.cc b/src/mysql/MySqlConnection.cc index f9532242ac..a49a203213 100644 --- a/src/mysql/MySqlConnection.cc +++ b/src/mysql/MySqlConnection.cc @@ -188,6 +188,19 @@ bool MySqlConnection::selectDb(std::string const& dbName) { return true; } +std::vector MySqlConnection::getColumnNames() const { + assert(_mysql); + assert(_mysql_res); + std::vector names; + if (0 != mysql_field_count(_mysql)) { + auto fields = mysql_fetch_fields(_mysql_res); + for (unsigned int i = 0; i < mysql_num_fields(_mysql_res); i++) { + names.push_back(std::string(fields[i].name)); + } + } + return names; +} + //////////////////////////////////////////////////////////////////////// // MySqlConnection // private: diff --git a/src/mysql/MySqlConnection.h b/src/mysql/MySqlConnection.h index fbe21133d7..b1c8001531 100644 --- a/src/mysql/MySqlConnection.h +++ b/src/mysql/MySqlConnection.h @@ -33,6 +33,7 @@ #include #include #include +#include // Third-party headers #include "boost/utility.hpp" @@ -80,6 +81,7 @@ class MySqlConnection : boost::noncopyable { assert(_mysql); return mysql_field_count(_mysql); } + std::vector getColumnNames() const; unsigned int getErrno() const { assert(_mysql); return mysql_errno(_mysql); diff --git a/src/mysql/MySqlUtils.cc b/src/mysql/MySqlUtils.cc new file mode 100644 index 0000000000..359239bff3 --- /dev/null +++ b/src/mysql/MySqlUtils.cc @@ -0,0 +1,95 @@ +// -*- LSST-C++ -*- +/* + * LSST Data Management System + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ + +// Class header +#include "mysql/MySqlUtils.h" + +// System headers +#include + +// Third party headers +#include +#include +#include + +// Qserv headers +#include "mysql/MySqlConfig.h" +#include "mysql/MySqlConnection.h" + +using namespace std; +using json = nlohmann::json; + +namespace { + +string errInfo(lsst::qserv::mysql::MySqlConnection const& conn) { + return "errno: " + to_string(conn.getErrno()) + ", error: " + conn.getError(); +} + +} // anonymous namespace + +namespace lsst::qserv::mysql { + +json MySqlUtils::processList(MySqlConfig const& config, bool full) { + string const context = "MySqlUtils::" + string(__func__); + string const query = "SHOW" + string(full ? " FULL" : "") + " PROCESSLIST"; + + MySqlConnection conn(config); + if (!conn.connect()) { + string const err = context + " failed to connect to the worker database, " + ::errInfo(conn); + throw MySqlQueryError(err); + } + if (!conn.queryUnbuffered(query)) { + string const err = "failed to execute the query: '" + query + "', " + ::errInfo(conn); + throw MySqlQueryError(err); + } + json result; + result["queries"] = json::object({{"columns", json::array()}, {"rows", json::array()}}); + int const numFields = conn.getResultFieldCount(); + if (numFields > 0) { + result["queries"]["columns"] = conn.getColumnNames(); + auto& rows = result["queries"]["rows"]; + MYSQL_RES* mysqlResult = conn.getResult(); + while (true) { + MYSQL_ROW mysqlRow = mysql_fetch_row(mysqlResult); + if (!mysqlRow) { + if (0 == conn.getErrno()) { + // End of iteration if no specific error was reported. + break; + } + string const err = + context + " failed to fetch next row for query: '" + query + "', " + ::errInfo(conn); + throw MySqlQueryError(err); + } + size_t const* lengths = mysql_fetch_lengths(mysqlResult); + json row = json::array(); + for (int i = 0; i < numFields; i++) { + // Report the empty string for SQL NULL. + auto const length = lengths[i]; + row.push_back(length == 0 ? string() : string(mysqlRow[i], length)); + } + rows.push_back(row); + } + } + return result; +} + +} // namespace lsst::qserv::mysql diff --git a/src/mysql/MySqlUtils.h b/src/mysql/MySqlUtils.h new file mode 100644 index 0000000000..933e701038 --- /dev/null +++ b/src/mysql/MySqlUtils.h @@ -0,0 +1,71 @@ +// -*- LSST-C++ -*- +/* + * LSST Data Management System + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ +#ifndef LSST_QSERV_MYSQL_MYSQLUTILS_H +#define LSST_QSERV_MYSQL_MYSQLUTILS_H + +// System headers +#include + +// Third-party headers +#include "nlohmann/json.hpp" + +/// Forward declarations. +namespace lsst::qserv::mysql { +class MySqlConfig; +} // namespace lsst::qserv::mysql + +/// This header declarations. +namespace lsst::qserv::mysql { + +/** + * Class MySqlQueryError represents exceptions to be throw on specific errors + * detected when attempting to execute the queries. + */ +class MySqlQueryError : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + +/** + * Class MySqlUtils is the utility class providing a collection of useful queries reporting + * small result sets. + * @note Each tool of the collection does its own connection handling (opening/etc.). + */ +class MySqlUtils { +public: + /** + * Report info on the on-going queries using 'SHOW [FULL] PROCESSLIST'. + * @param A scope of the operaton depends on the user credentials privided + * in the configuration object. Normally, a subset of queries which belong + * to the specified user will be reported. + * @param config Configuration parameters of the MySQL connector. + * @param full The optional modifier which (if set) allows seeing the full text + * of the queries. + * @return A collection of queries encoded as the JSON object. Please, see the code + * for further details on the schema of the object. + * @throws MySqlQueryError on errors detected during query execution/processing. + */ + static nlohmann::json processList(MySqlConfig const& config, bool full = false); +}; + +} // namespace lsst::qserv::mysql + +#endif // LSST_QSERV_MYSQL_MYSQLUTILS_H From bc0ca76c414aa8cb3160e9853a6f5b648cc222ca Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Tue, 1 Aug 2023 20:49:57 +0000 Subject: [PATCH 02/12] Moderate refactoring and code consolidation in the worker requests Simplified error reporting. Reduced code duplication. --- src/proto/worker.proto | 171 +++++--------------- src/replica/AddReplicaQservMgtRequest.cc | 24 ++- src/replica/GetReplicasQservMgtRequest.cc | 21 +-- src/replica/GetStatusQservMgtRequest.cc | 15 +- src/replica/RemoveReplicaQservMgtRequest.cc | 25 ++- src/replica/SetReplicasQservMgtRequest.cc | 25 ++- src/replica/TestEchoQservMgtRequest.cc | 23 ++- src/wbase/WorkerCommand.cc | 14 +- src/wbase/WorkerCommand.h | 40 +++-- src/wpublish/AddChunkGroupCommand.cc | 37 ++--- src/wpublish/AddChunkGroupCommand.h | 18 +-- src/wpublish/ChunkListCommand.cc | 41 ++--- src/wpublish/ChunkListCommand.h | 14 +- src/wpublish/GetChunkListCommand.cc | 5 +- src/wpublish/GetChunkListCommand.h | 1 + src/wpublish/GetStatusCommand.cc | 1 + src/wpublish/GetStatusCommand.h | 1 + src/wpublish/RemoveChunkGroupCommand.cc | 49 ++---- src/wpublish/RemoveChunkGroupCommand.h | 15 +- src/wpublish/SetChunkListCommand.cc | 51 +++--- src/wpublish/SetChunkListCommand.h | 19 +-- src/wpublish/TestEchoCommand.cc | 2 +- src/wpublish/TestEchoCommand.h | 1 + src/xrdreq/ChunkGroupQservRequest.cc | 43 +---- src/xrdreq/ChunkGroupQservRequest.h | 16 +- src/xrdreq/ChunkListQservRequest.cc | 34 +--- src/xrdreq/ChunkListQservRequest.h | 14 +- src/xrdreq/GetChunkListQservRequest.cc | 34 +--- src/xrdreq/GetChunkListQservRequest.h | 14 +- src/xrdreq/GetStatusQservRequest.cc | 19 +-- src/xrdreq/GetStatusQservRequest.h | 14 +- src/xrdreq/QservRequest.h | 2 +- src/xrdreq/QueryManagementAction.cc | 5 +- src/xrdreq/QueryManagementRequest.cc | 20 +-- src/xrdreq/QueryManagementRequest.h | 13 +- src/xrdreq/SetChunkListQservRequest.cc | 42 +---- src/xrdreq/SetChunkListQservRequest.h | 16 +- src/xrdreq/TestEchoQservRequest.cc | 34 +--- src/xrdreq/TestEchoQservRequest.h | 14 +- src/xrdreq/qserv-query-management.cc | 7 +- src/xrdreq/qserv-worker-notify.cc | 67 ++++---- src/xrdreq/qserv-worker-perf-chunks.cc | 25 +-- src/xrdreq/qserv-worker-perf.cc | 17 +- src/xrdreq/qserv-worker-status.cc | 23 ++- 44 files changed, 314 insertions(+), 772 deletions(-) diff --git a/src/proto/worker.proto b/src/proto/worker.proto index 74be7d4126..548591bc51 100644 --- a/src/proto/worker.proto +++ b/src/proto/worker.proto @@ -148,67 +148,45 @@ message Result { // sending this message carrying an identifier of a command. Specific // commands may require additional parameters which should be sent as // separate messages (of the corresponding types). -// message WorkerCommandH { - - // Types of commands enum Command { - - // Return back a value passed as an input parameters - // The command is meant for testing the protocol - TEST_ECHO = 1; - - // Add a group of collocated chunks - ADD_CHUNK_GROUP = 2; - - // Remove a group of collocated chunks - REMOVE_CHUNK_GROUP = 3; - - // Update (rebuild and/or reload) the list of available chunks - UPDATE_CHUNK_LIST = 4; - - // Return a list of chunks known to a worker - GET_CHUNK_LIST = 5; - - // Set a new list of chunks - SET_CHUNK_LIST = 6; - - // Return various status info on a worker - GET_STATUS = 7; + TEST_ECHO = 1; // Return back a value sent to the command processor. + ADD_CHUNK_GROUP = 2; // Add a group of collocated chunks. + REMOVE_CHUNK_GROUP = 3; // Remove a group of collocated chunks. + UPDATE_CHUNK_LIST = 4; // Update (rebuild and/or reload) the list of available chunks. + GET_CHUNK_LIST = 5; // Return a list of chunks known to a worker. + SET_CHUNK_LIST = 6; // Set a new list of chunks. + GET_STATUS = 7; // Return various status info on a worker. } required Command command = 1; } +// The completion status to be sent back with responses to the worker commands. +message WorkerCommandStatus { + enum Code { + SUCCESS = 1; // The sccessful completion of a request. + INVALID = 2; // Invalid parameters of the request. + IN_USE = 3; // The request is rejected because one of the chunks is in use. + ERROR = 4; // An error occurred during command execution. + } + optional Code code = 1 [default = SUCCESS]; + optional string error = 2 [default = ""]; // Optional error message (depends on the code) +} + // This message must be sent after the command header to provide // a service with a value to be echoed back in response to // the 'TEST_ECHO' command. -// message WorkerCommandTestEchoM { - - // An input string to be returned back by the service - required string value = 1; + required string value = 1; // The input string to be returned back by the service } // The message to be sent back in response to the 'TEST_ECHO' command -// message WorkerCommandTestEchoR { - - // Completion status of the operation - enum Status { - SUCCESS = 1; // successful completion of a request - ERROR = 2; // an error occurred during command execution - } - required Status status = 1; - - // Optional error message (depending on the status) - optional string error = 2 [default = ""]; - - // The original value returned by the operation - required string value = 3; + required WorkerCommandStatus status = 4; // Completion status of the operation + required string value = 3; // The original value returned by the operation } // The message type embedded into the relevant contexts below -// message WorkerCommandChunk { required string db = 1; required uint32 chunk = 2; @@ -218,119 +196,57 @@ message WorkerCommandChunk { // This message must be sent after the command header for the 'ADD_CHUNK_GROUP' // or 'REMOVE_CHUNK_GROUP' command to tell the service which chunks needs to be // added or removed. -// message WorkerCommandChunkGroupM { - required uint32 chunk = 1; repeated string dbs = 2; - optional bool force = 3 [ default = false]; } // The message to be sent back in response to the 'ADD_CHUNK_GROUP' // or 'REMOVE_CHUNK_GROUP' commands. -// message WorkerCommandChunkGroupR { - - // Completion status of the operation - enum Status { - SUCCESS = 1; // successful completion of a request - INVALID = 2; // invalid parameters of the request - IN_USE = 3; // request is rejected because one of the chunks is in use - ERROR = 4; // an error occurred during command execution - } - required Status status = 1; - - // Optional error message (depending on the status) - optional string error = 2 [default = ""]; + required WorkerCommandStatus status = 3; // Completion status of the operation } // This message must be sent after the command header for the 'UPDATE_CHUNK_LIST' -// command -// +// command. message WorkerCommandUpdateChunkListM { - - // Rebuild the list from actual tables existing in the database - required bool rebuild = 1; - - // Reload the new list into a worker - required bool reload = 2; + required bool rebuild = 1; // Rebuild the list from actual tables existing in the database + required bool reload = 2; // Reload the new list into a worker } // The message to be sent back in response to the 'UPDATE_CHUNK_LIST' // command. -// message WorkerCommandUpdateChunkListR { - - // Completion status of the operation - enum Status { - SUCCESS = 1; // successful completion of a request - ERROR = 2; // an error occurred during command execution - } - required Status status = 1; - - // Optional error message (depending on the status) - optional string error = 2 [default = ""]; - - repeated WorkerCommandChunk added = 3; // chunks which have been added - repeated WorkerCommandChunk removed = 4; // chunks which have been removed + required WorkerCommandStatus status = 5; // Completion status of the operation + repeated WorkerCommandChunk added = 3; // Chunks that were added + repeated WorkerCommandChunk removed = 4; // Chunks that were removed } // The message to be sent back in response to the 'GET_CHUNK_LIST' // command. -// message WorkerCommandGetChunkListR { - - // Completion status of the operation - enum Status { - SUCCESS = 1; // successful completion of a request - ERROR = 2; // an error occurred during command execution - } - required Status status = 1; - - // Optional error message (depending on the status) - optional string error = 2 [default = ""]; - + required WorkerCommandStatus status = 4; // Completion status of the operation repeated WorkerCommandChunk chunks = 3; } // This message must be sent after the command header for the 'SET_CHUNK_LIST' // to tell the service which chunks needs to be set. -// message WorkerCommandSetChunkListM { - repeated WorkerCommandChunk chunks = 1; - optional bool force = 2 [ default = false]; - - // The operation involves databases which are listed below - repeated string databases = 3; + repeated string databases = 3; // The operation involves databases listed here } // The message to be sent back in response to the 'SET_CHUNK_LIST' // command. -// message WorkerCommandSetChunkListR { - - // Completion status of the operation - enum Status { - SUCCESS = 1; // successful completion of a request - INVALID = 2; // invalid parameters of the request - IN_USE = 3; // request is rejected because one of the chunks is in use - ERROR = 4; // an error occurred during command execution - } - required Status status = 1; - - // Optional error message (depending on the status) - optional string error = 2 [default = ""]; - - // The previous list of chunks - repeated WorkerCommandChunk chunks = 3; + required WorkerCommandStatus status = 4; // Completion status of the operation + repeated WorkerCommandChunk chunks = 3; // The previous list of chunks } // This message must be sent after the command header for the 'GET_STATUS' // to customize a scope of the request. -// message WorkerCommandGetStatusM { // Include detailed info on the tasks optional bool include_tasks = 1 [ default = false]; @@ -353,14 +269,11 @@ message WorkerCommandGetStatusM { } // The message to be sent back in response to the 'GET_STATUS' command -// message WorkerCommandGetStatusR { - - // Status info serialized from a JSON object - required string info = 1; + required WorkerCommandStatus status = 2; // Completion status of the operation + required string info = 1; // Status info serialized from a JSON object } - ///////////////////////////////////////////////////////////////// // Protocol definition for the query management requests. These // requests do not require any response messages to be explicitly @@ -372,18 +285,10 @@ message WorkerCommandGetStatusR { //////////////////////////////////////////////////////////////// message QueryManagement { - - // Supported operations enum Operation { - - // Cancel older queries before the specified query (excluding that one). - CANCEL_AFTER_RESTART = 1; - - // Cancel a specific query. - CANCEL = 2; - - // Notify workers on the completion of the specified query. - COMPLETE = 3; + CANCEL_AFTER_RESTART = 1; // Cancel older queries before the specified query (excluding that one). + CANCEL = 2; // Cancel a specific query. + COMPLETE = 3; // Notify workers on the completion of the specified query. } required Operation op = 1; required uint64 query_id = 2; diff --git a/src/replica/AddReplicaQservMgtRequest.cc b/src/replica/AddReplicaQservMgtRequest.cc index 80458cba0d..e85d9264e5 100644 --- a/src/replica/AddReplicaQservMgtRequest.cc +++ b/src/replica/AddReplicaQservMgtRequest.cc @@ -28,6 +28,7 @@ // Qserv headers #include "global/ResourceUnit.h" +#include "proto/worker.pb.h" #include "replica/Configuration.h" #include "replica/ServiceProvider.h" @@ -74,35 +75,28 @@ void AddReplicaQservMgtRequest::startImpl(replica::Lock const& lock) { auto const request = shared_from_base(); _qservRequest = xrdreq::AddChunkGroupQservRequest::create( - chunk(), databases(), - [request](xrdreq::ChunkGroupQservRequest::Status status, string const& error) { + chunk(), databases(), [request](proto::WorkerCommandStatus::Code code, string const& error) { if (request->state() == State::FINISHED) return; - replica::Lock lock(request->_mtx, request->context() + string(__func__) + "[callback]"); - if (request->state() == State::FINISHED) return; - switch (status) { - case xrdreq::ChunkGroupQservRequest::Status::SUCCESS: + switch (code) { + case proto::WorkerCommandStatus::SUCCESS: request->finish(lock, QservMgtRequest::ExtendedState::SUCCESS); break; - - case xrdreq::ChunkGroupQservRequest::Status::INVALID: + case proto::WorkerCommandStatus::INVALID: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_BAD, error); break; - - case xrdreq::ChunkGroupQservRequest::Status::IN_USE: + case proto::WorkerCommandStatus::IN_USE: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_CHUNK_IN_USE, error); break; - - case xrdreq::ChunkGroupQservRequest::Status::ERROR: + case proto::WorkerCommandStatus::ERROR: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_ERROR, error); break; - default: throw logic_error("AddReplicaQservMgtRequest::" + string(__func__) + - " unhandled server status: " + - xrdreq::ChunkGroupQservRequest::status2str(status)); + " unhandled request completion code: " + + proto::WorkerCommandStatus_Code_Name(code)); } }); XrdSsiResource resource(ResourceUnit::makeWorkerPath(worker())); diff --git a/src/replica/GetReplicasQservMgtRequest.cc b/src/replica/GetReplicasQservMgtRequest.cc index 6b10a39fab..57c88e21a0 100644 --- a/src/replica/GetReplicasQservMgtRequest.cc +++ b/src/replica/GetReplicasQservMgtRequest.cc @@ -32,6 +32,7 @@ // Qserv headers #include "global/ResourceUnit.h" +#include "proto/worker.pb.h" #include "replica/Configuration.h" #include "replica/ServiceProvider.h" @@ -112,30 +113,24 @@ void GetReplicasQservMgtRequest::startImpl(replica::Lock const& lock) { auto const request = shared_from_base(); _qservRequest = xrdreq::GetChunkListQservRequest::create( - inUseOnly(), [request](xrdreq::GetChunkListQservRequest::Status status, string const& error, + inUseOnly(), [request](proto::WorkerCommandStatus::Code code, string const& error, xrdreq::GetChunkListQservRequest::ChunkCollection const& collection) { if (request->state() == State::FINISHED) return; - replica::Lock lock(request->_mtx, request->context() + string(__func__) + "[callback]"); - if (request->state() == State::FINISHED) return; - switch (status) { - case xrdreq::GetChunkListQservRequest::Status::SUCCESS: - + switch (code) { + case proto::WorkerCommandStatus::SUCCESS: request->_setReplicas(lock, collection); request->finish(lock, QservMgtRequest::ExtendedState::SUCCESS); break; - - case xrdreq::GetChunkListQservRequest::Status::ERROR: - + case proto::WorkerCommandStatus::ERROR: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_ERROR, error); break; - default: - throw logic_error("GetReplicasQservMgtRequest::" + string(__func__) + - " unhandled server status: " + - xrdreq::GetChunkListQservRequest::status2str(status)); + throw logic_error( + "GetReplicasQservMgtRequest::" + string(__func__) + + " unhandled server status: " + proto::WorkerCommandStatus_Code_Name(code)); } }); XrdSsiResource resource(ResourceUnit::makeWorkerPath(worker())); diff --git a/src/replica/GetStatusQservMgtRequest.cc b/src/replica/GetStatusQservMgtRequest.cc index ba43289a61..06a3bec11e 100644 --- a/src/replica/GetStatusQservMgtRequest.cc +++ b/src/replica/GetStatusQservMgtRequest.cc @@ -32,6 +32,7 @@ // Qserv headers #include "global/ResourceUnit.h" +#include "proto/worker.pb.h" #include "replica/Configuration.h" #include "replica/ServiceProvider.h" @@ -81,13 +82,13 @@ void GetStatusQservMgtRequest::startImpl(replica::Lock const& lock) { auto const request = shared_from_base(); _qservRequest = xrdreq::GetStatusQservRequest::create( _taskSelector, - [request](xrdreq::GetStatusQservRequest::Status status, string const& error, string const& info) { + [request](proto::WorkerCommandStatus::Code code, string const& error, string const& info) { if (request->state() == State::FINISHED) return; replica::Lock const lock(request->_mtx, request->context() + string(__func__) + "[callback]"); if (request->state() == State::FINISHED) return; - switch (status) { - case xrdreq::GetStatusQservRequest::Status::SUCCESS: + switch (code) { + case proto::WorkerCommandStatus::SUCCESS: try { request->_setInfo(lock, info); request->finish(lock, QservMgtRequest::ExtendedState::SUCCESS); @@ -98,13 +99,13 @@ void GetStatusQservMgtRequest::startImpl(replica::Lock const& lock) { request->finish(lock, QservMgtRequest::ExtendedState::SERVER_BAD_RESPONSE, msg); } break; - case xrdreq::GetStatusQservRequest::Status::ERROR: + case proto::WorkerCommandStatus::ERROR: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_ERROR, error); break; default: - throw logic_error("GetStatusQservMgtRequest::" + string(__func__) + - " unhandled server status: " + - xrdreq::GetStatusQservRequest::status2str(status)); + throw logic_error( + "GetStatusQservMgtRequest::" + string(__func__) + + " unhandled server status: " + proto::WorkerCommandStatus_Code_Name(code)); } }); XrdSsiResource resource(ResourceUnit::makeWorkerPath(worker())); diff --git a/src/replica/RemoveReplicaQservMgtRequest.cc b/src/replica/RemoveReplicaQservMgtRequest.cc index 83e4de4f30..d44e6a551c 100644 --- a/src/replica/RemoveReplicaQservMgtRequest.cc +++ b/src/replica/RemoveReplicaQservMgtRequest.cc @@ -28,6 +28,7 @@ // Qserv headers #include "global/ResourceUnit.h" +#include "proto/worker.pb.h" #include "replica/Configuration.h" #include "replica/ServiceProvider.h" @@ -78,34 +79,28 @@ void RemoveReplicaQservMgtRequest::startImpl(replica::Lock const& lock) { _qservRequest = xrdreq::RemoveChunkGroupQservRequest::create( chunk(), databases(), force(), - [request](xrdreq::ChunkGroupQservRequest::Status status, string const& error) { + [request](proto::WorkerCommandStatus::Code code, string const& error) { if (request->state() == State::FINISHED) return; - replica::Lock lock(request->_mtx, request->context() + string(__func__) + "[callback]"); - if (request->state() == State::FINISHED) return; - switch (status) { - case xrdreq::ChunkGroupQservRequest::Status::SUCCESS: + switch (code) { + case proto::WorkerCommandStatus::SUCCESS: request->finish(lock, QservMgtRequest::ExtendedState::SUCCESS); break; - - case xrdreq::ChunkGroupQservRequest::Status::INVALID: + case proto::WorkerCommandStatus::INVALID: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_BAD, error); break; - - case xrdreq::ChunkGroupQservRequest::Status::IN_USE: + case proto::WorkerCommandStatus::IN_USE: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_CHUNK_IN_USE, error); break; - - case xrdreq::ChunkGroupQservRequest::Status::ERROR: + case proto::WorkerCommandStatus::ERROR: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_ERROR, error); break; - default: - throw logic_error("RemoveReplicaQservMgtRequest::" + string(__func__) + - " unhandled server status: " + - xrdreq::ChunkGroupQservRequest::status2str(status)); + throw logic_error( + "RemoveReplicaQservMgtRequest::" + string(__func__) + + " unhandled server status: " + proto::WorkerCommandStatus_Code_Name(code)); } }); XrdSsiResource resource(ResourceUnit::makeWorkerPath(worker())); diff --git a/src/replica/SetReplicasQservMgtRequest.cc b/src/replica/SetReplicasQservMgtRequest.cc index f5badd1f95..c594804300 100644 --- a/src/replica/SetReplicasQservMgtRequest.cc +++ b/src/replica/SetReplicasQservMgtRequest.cc @@ -33,6 +33,7 @@ // Qserv headers #include "global/ResourceUnit.h" +#include "proto/worker.pb.h" #include "replica/Configuration.h" #include "replica/ServiceProvider.h" #include "util/IterableFormatter.h" @@ -106,36 +107,30 @@ void SetReplicasQservMgtRequest::startImpl(replica::Lock const& lock) { _qservRequest = xrdreq::SetChunkListQservRequest::create( chunks, _databases, force(), - [request](xrdreq::SetChunkListQservRequest::Status status, string const& error, + [request](proto::WorkerCommandStatus::Code code, string const& error, xrdreq::SetChunkListQservRequest::ChunkCollection const& collection) { if (request->state() == State::FINISHED) return; - replica::Lock lock(request->_mtx, request->context() + string(__func__) + "[callback]"); - if (request->state() == State::FINISHED) return; - switch (status) { - case xrdreq::SetChunkListQservRequest::Status::SUCCESS: + switch (code) { + case proto::WorkerCommandStatus::SUCCESS: request->_setReplicas(lock, collection); request->finish(lock, QservMgtRequest::ExtendedState::SUCCESS); break; - - case xrdreq::SetChunkListQservRequest::Status::ERROR: + case proto::WorkerCommandStatus::ERROR: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_ERROR, error); break; - - case xrdreq::SetChunkListQservRequest::Status::INVALID: + case proto::WorkerCommandStatus::INVALID: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_BAD, error); break; - - case xrdreq::SetChunkListQservRequest::Status::IN_USE: + case proto::WorkerCommandStatus::IN_USE: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_CHUNK_IN_USE, error); break; - default: - throw logic_error("SetReplicasQservMgtRequest:: " + string(__func__) + - " unhandled server status: " + - xrdreq::SetChunkListQservRequest::status2str(status)); + throw logic_error( + "SetReplicasQservMgtRequest:: " + string(__func__) + + " unhandled server status: " + proto::WorkerCommandStatus_Code_Name(code)); } }); XrdSsiResource resource(ResourceUnit::makeWorkerPath(worker())); diff --git a/src/replica/TestEchoQservMgtRequest.cc b/src/replica/TestEchoQservMgtRequest.cc index b84988de56..d5460bdc61 100644 --- a/src/replica/TestEchoQservMgtRequest.cc +++ b/src/replica/TestEchoQservMgtRequest.cc @@ -32,6 +32,7 @@ // Qserv headers #include "global/ResourceUnit.h" +#include "proto/worker.pb.h" #include "replica/Configuration.h" #include "replica/ServiceProvider.h" @@ -79,30 +80,24 @@ void TestEchoQservMgtRequest::startImpl(replica::Lock const& lock) { auto const request = shared_from_base(); _qservRequest = xrdreq::TestEchoQservRequest::create( - data(), [request](xrdreq::TestEchoQservRequest::Status status, string const& error, - string const& data, string const& dataEcho) { + data(), [request](proto::WorkerCommandStatus::Code code, string const& error, string const& data, + string const& dataEcho) { if (request->state() == State::FINISHED) return; - replica::Lock lock(request->_mtx, request->context() + string(__func__) + "[callback]"); - if (request->state() == State::FINISHED) return; - switch (status) { - case xrdreq::TestEchoQservRequest::Status::SUCCESS: - + switch (code) { + case proto::WorkerCommandStatus::SUCCESS: request->_setData(lock, dataEcho); request->finish(lock, QservMgtRequest::ExtendedState::SUCCESS); break; - - case xrdreq::TestEchoQservRequest::Status::ERROR: - + case proto::WorkerCommandStatus::ERROR: request->finish(lock, QservMgtRequest::ExtendedState::SERVER_ERROR, error); break; - default: - throw logic_error("TestEchoQservMgtRequest::" + string(__func__) + - " unhandled server status: " + - xrdreq::TestEchoQservRequest::status2str(status)); + throw logic_error( + "TestEchoQservMgtRequest::" + string(__func__) + + " unhandled server status: " + proto::WorkerCommandStatus_Code_Name(code)); } }); XrdSsiResource resource(ResourceUnit::makeWorkerPath(worker())); diff --git a/src/wbase/WorkerCommand.cc b/src/wbase/WorkerCommand.cc index 63cf533cb8..cf79089a92 100644 --- a/src/wbase/WorkerCommand.cc +++ b/src/wbase/WorkerCommand.cc @@ -24,8 +24,6 @@ // Class header #include "wbase/WorkerCommand.h" -// Third-party headers - // LSST headers #include "lsst/log/Log.h" @@ -40,12 +38,12 @@ LOG_LOGGER _log = LOG_GET("lsst.qserv.wbase.WorkerCommand"); namespace lsst::qserv::wbase { -WorkerCommand::WorkerCommand(SendChannel::Ptr const& sendChannel) : _sendChannel(sendChannel) { - // Register a function which will run a subclass-specific - // implementation of method run() - setFunc([this](util::CmdData* data) { this->run(); }); -} +WorkerCommand::WorkerCommand(SendChannel::Ptr const& sendChannel) + : util::Command([this](util::CmdData* data) { this->run(); }), _sendChannel(sendChannel) {} -WorkerCommand::~WorkerCommand() {} +void WorkerCommand::sendSerializedResponse() { + std::string str(_frameBuf.data(), _frameBuf.size()); + _sendChannel->sendStream(xrdsvc::StreamBuffer::createWithMove(str), true); +} } // namespace lsst::qserv::wbase diff --git a/src/wbase/WorkerCommand.h b/src/wbase/WorkerCommand.h index 402ea51955..c0934f4797 100644 --- a/src/wbase/WorkerCommand.h +++ b/src/wbase/WorkerCommand.h @@ -25,12 +25,14 @@ #define LSST_QSERV_WBASE_WORKER_COMMAND_H // System headers +#include #include #include #include // Qserv headers #include "proto/FrameBuffer.h" +#include "proto/worker.pb.h" #include "util/Command.h" // Forward declarations @@ -46,29 +48,45 @@ namespace lsst::qserv::wbase { */ class WorkerCommand : public util::Command { public: - /// The smart pointer type to objects of the class using Ptr = std::shared_ptr; - // The default construction and copy semantics are prohibited WorkerCommand& operator=(const WorkerCommand&) = delete; WorkerCommand(const WorkerCommand&) = delete; WorkerCommand() = delete; + virtual ~WorkerCommand() = default; - /** - * The normal constructor of the class - * @param sendChannel - communication channel for reporting results - */ + /// @param sendChannel - communication channel for reporting results explicit WorkerCommand(std::shared_ptr const& sendChannel); - /// The destructor - virtual ~WorkerCommand(); +protected: + /// The actual behavior is provided by subclasses. + virtual void run() = 0; /** - * The code which will be execute by specific subclasses of this abstract class + * Fill in the status code and the message into the response message + * of the desired type and sent it back to a caller. + * @param error Mandatory error to be reported. + * @param code The optional error code if the one differes from the default one. + * @param extendedModsFunc The optional function to be provided if any additional modifications + * are required to be made to the response object. */ - virtual void run() = 0; + template + void reportError(std::string const& error, + proto::WorkerCommandStatus::Code code = proto::WorkerCommandStatus::ERROR, + std::function const& extendedModsFunc = nullptr) { + RESPONSE resp; + resp.mutable_status()->set_code(code); + resp.mutable_status()->set_error(error); + if (extendedModsFunc != nullptr) extendedModsFunc(resp); + _frameBuf.serialize(resp); + sendSerializedResponse(); + } + + /** + * Send the serialized payload stored within the frame buffer to a caller. + */ + void sendSerializedResponse(); -protected: std::shared_ptr _sendChannel; ///< For result reporting proto::FrameBuffer _frameBuf; ///< Buffer for serializing a response }; diff --git a/src/wpublish/AddChunkGroupCommand.cc b/src/wpublish/AddChunkGroupCommand.cc index 3523027ce9..6b7853652c 100644 --- a/src/wpublish/AddChunkGroupCommand.cc +++ b/src/wpublish/AddChunkGroupCommand.cc @@ -58,28 +58,14 @@ AddChunkGroupCommand::AddChunkGroupCommand(shared_ptr const& _chunk(chunk), _databases(databases) {} -void AddChunkGroupCommand::_reportError(proto::WorkerCommandChunkGroupR::Status status, - string const& message) { - LOGS(_log, LOG_LVL_ERROR, "AddChunkGroupCommand::" << __func__ << " " << message); - - proto::WorkerCommandChunkGroupR reply; - - reply.set_status(status); - reply.set_error(message); - - _frameBuf.serialize(reply); - string str(_frameBuf.data(), _frameBuf.size()); - _sendChannel->sendStream(xrdsvc::StreamBuffer::createWithMove(str), true); -} - void AddChunkGroupCommand::run() { string const context = "AddChunkGroupCommand::" + string(__func__) + " "; - LOGS(_log, LOG_LVL_DEBUG, context); - if (not _databases.size()) { - _reportError(proto::WorkerCommandChunkGroupR::INVALID, - "the list of database names in the group was found empty"); + if (_databases.empty()) { + reportError( + "the list of database names in the group was found empty", + proto::WorkerCommandStatus::INVALID); return; } @@ -87,16 +73,11 @@ void AddChunkGroupCommand::run() { dynamic_cast(XrdSsiProviderLookup); XrdSsiCluster* clusterManager = providerServer->GetClusterManager(); - proto::WorkerCommandChunkGroupR reply; - reply.set_status(proto::WorkerCommandChunkGroupR::SUCCESS); - for (auto&& database : _databases) { string const resource = "/chk/" + database + "/" + to_string(_chunk); - LOGS(_log, LOG_LVL_DEBUG, context << " adding the chunk resource: " << resource << " in DataContext=" << clusterManager->DataContext()); - try { // Notify XRootD/cmsd and (depending on a mode) modify the provider's copy // of the inventory. @@ -104,22 +85,22 @@ void AddChunkGroupCommand::run() { if (clusterManager->DataContext()) { providerServer->GetChunkInventory().add(database, _chunk); } - // Notify QServ and update the database _chunkInventory->add(database, _chunk, _mySqlConfig); } catch (InvalidParamError const& ex) { - _reportError(proto::WorkerCommandChunkGroupR::INVALID, ex.what()); + reportError(ex.what(), proto::WorkerCommandStatus::INVALID); return; } catch (QueryError const& ex) { - _reportError(proto::WorkerCommandChunkGroupR::ERROR, ex.what()); + reportError(ex.what()); return; } catch (exception const& ex) { - _reportError(proto::WorkerCommandChunkGroupR::ERROR, - "failed to add the chunk: " + string(ex.what())); + reportError("failed to add the chunk: " + string(ex.what())); return; } } + proto::WorkerCommandChunkGroupR reply; + reply.mutable_status(); _frameBuf.serialize(reply); string str(_frameBuf.data(), _frameBuf.size()); _sendChannel->sendStream(xrdsvc::StreamBuffer::createWithMove(str), true); diff --git a/src/wpublish/AddChunkGroupCommand.h b/src/wpublish/AddChunkGroupCommand.h index 8193a215ea..fc97c682aa 100644 --- a/src/wpublish/AddChunkGroupCommand.h +++ b/src/wpublish/AddChunkGroupCommand.h @@ -52,14 +52,11 @@ namespace lsst::qserv::wpublish { */ class AddChunkGroupCommand : public wbase::WorkerCommand { public: - // The default construction and copy semantics are prohibited AddChunkGroupCommand& operator=(const AddChunkGroupCommand&) = delete; AddChunkGroupCommand(const AddChunkGroupCommand&) = delete; AddChunkGroupCommand() = delete; /** - * The normal constructor of the class - * * @param sendChannel communication channel for reporting results * @param chunkInventory chunks known to the application * @param mySqlConfig database connection parameters @@ -71,19 +68,10 @@ class AddChunkGroupCommand : public wbase::WorkerCommand { mysql::MySqlConfig const& mySqlConfig, int chunk, std::vector const& databases); - ~AddChunkGroupCommand() override = default; + virtual ~AddChunkGroupCommand() override = default; - void run() override; - -private: - /** - * Report error condition to the logging stream and reply back to - * a service caller. - * - * @param status error status - * @param message message to be reported - */ - void _reportError(proto::WorkerCommandChunkGroupR::Status status, std::string const& message); +protected: + virtual void run() override; private: std::shared_ptr _chunkInventory; diff --git a/src/wpublish/ChunkListCommand.cc b/src/wpublish/ChunkListCommand.cc index a9502b0d50..0711352991 100644 --- a/src/wpublish/ChunkListCommand.cc +++ b/src/wpublish/ChunkListCommand.cc @@ -66,26 +66,12 @@ ChunkListCommand::ChunkListCommand(shared_ptr const& sendCha _rebuild(rebuild), _reload(reload) {} -void ChunkListCommand::_reportError(string const& message) { - LOGS(_log, LOG_LVL_ERROR, "ChunkListCommand::" << __func__ << " " << message); - - proto::WorkerCommandUpdateChunkListR reply; - - reply.set_status(proto::WorkerCommandUpdateChunkListR::ERROR); - reply.set_error(message); - - _frameBuf.serialize(reply); - string str(_frameBuf.data(), _frameBuf.size()); - _sendChannel->sendStream(xrdsvc::StreamBuffer::createWithMove(str), true); -} - void ChunkListCommand::run() { string const context = "ChunkListCommand::" + string(__func__) + " "; - LOGS(_log, LOG_LVL_DEBUG, context); proto::WorkerCommandUpdateChunkListR reply; - reply.set_status(proto::WorkerCommandUpdateChunkListR::SUCCESS); + reply.mutable_status(); // Rebuild persistent list if requested if (_rebuild) { @@ -94,7 +80,8 @@ void ChunkListCommand::run() { xrdsvc::XrdName x; newChunkInventory.rebuild(x.getName(), _mySqlConfig); } catch (exception const& ex) { - _reportError("database operation failed: " + string(ex.what())); + reportError("database operation failed: " + + string(ex.what())); return; } } @@ -107,7 +94,8 @@ void ChunkListCommand::run() { xrdsvc::XrdName x; newChunkInventory.init(x.getName(), _mySqlConfig); } catch (exception const& ex) { - _reportError("database operation failed: " + string(ex.what())); + reportError("database operation failed: " + + string(ex.what())); return; } ::dumpInventory(*_chunkInventory, context + "_chunkInventory: "); @@ -116,7 +104,6 @@ void ChunkListCommand::run() { // Compare two maps and worker identifiers to see which resources were // were added or removed. Then Update the current map and notify XRootD // accordingly. - ChunkInventory::ExistMap const removedChunks = *_chunkInventory - newChunkInventory; ChunkInventory::ExistMap const addedChunks = newChunkInventory - *_chunkInventory; @@ -127,14 +114,11 @@ void ChunkListCommand::run() { if (not removedChunks.empty()) { for (auto&& entry : removedChunks) { string const& database = entry.first; - for (int chunk : entry.second) { string const resource = "/chk/" + database + "/" + to_string(chunk); - LOGS(_log, LOG_LVL_DEBUG, context << "removing resource: " << resource << " in DataContext=" << clusterManager->DataContext()); - try { // Notify XRootD/cmsd and (depending on a mode) modify the provider's copy // of the inventory. @@ -142,15 +126,13 @@ void ChunkListCommand::run() { if (clusterManager->DataContext()) { providerServer->GetChunkInventory().remove(database, chunk); } - // Notify QServ _chunkInventory->remove(database, chunk); - } catch (exception const& ex) { - _reportError("failed to remove the chunk: " + string(ex.what())); + reportError("failed to remove the chunk: " + + string(ex.what())); return; } - // Notify the caller of this service proto::WorkerCommandChunk* ptr = reply.add_removed(); ptr->set_db(database); @@ -161,14 +143,11 @@ void ChunkListCommand::run() { if (not addedChunks.empty()) { for (auto&& entry : addedChunks) { string const& database = entry.first; - for (int chunk : entry.second) { string const resource = "/chk/" + database + "/" + to_string(chunk); - LOGS(_log, LOG_LVL_DEBUG, context + "adding resource: " << resource << " in DataContext=" << clusterManager->DataContext()); - try { // Notify XRootD/cmsd and (depending on a mode) modify the provider's copy // of the inventory. @@ -176,15 +155,13 @@ void ChunkListCommand::run() { if (clusterManager->DataContext()) { providerServer->GetChunkInventory().add(database, chunk); } - // Notify QServ _chunkInventory->add(database, chunk); - } catch (exception const& ex) { - _reportError("failed to add the chunk: " + string(ex.what())); + reportError("failed to add the chunk: " + + string(ex.what())); return; } - // Notify the caller of this service proto::WorkerCommandChunk* ptr = reply.add_added(); ptr->set_db(database); diff --git a/src/wpublish/ChunkListCommand.h b/src/wpublish/ChunkListCommand.h index ae799de66f..3cbe3cfb6d 100644 --- a/src/wpublish/ChunkListCommand.h +++ b/src/wpublish/ChunkListCommand.h @@ -43,12 +43,10 @@ namespace lsst::qserv::wpublish { */ class ChunkListCommand : public wbase::WorkerCommand { public: - // The default construction and copy semantics are prohibited ChunkListCommand() = delete; ChunkListCommand& operator=(ChunkListCommand const&) = delete; ChunkListCommand(ChunkListCommand const&) = delete; - - ~ChunkListCommand() override = default; + virtual ~ChunkListCommand() override = default; protected: /** @@ -62,17 +60,9 @@ class ChunkListCommand : public wbase::WorkerCommand { std::shared_ptr const& chunkInventory, mysql::MySqlConfig const& mySqlConfig, bool rebuild, bool reload); - void run() override; + virtual void run() override; private: - /** - * Report error condition to the logging stream and reply back to - * a service caller. - * - * @param message message to be reported - */ - void _reportError(std::string const& message); - // Parameters of the object std::shared_ptr _chunkInventory; diff --git a/src/wpublish/GetChunkListCommand.cc b/src/wpublish/GetChunkListCommand.cc index ce6e550b8e..2471bc2624 100644 --- a/src/wpublish/GetChunkListCommand.cc +++ b/src/wpublish/GetChunkListCommand.cc @@ -54,10 +54,8 @@ void GetChunkListCommand::run() { LOGS(_log, LOG_LVL_DEBUG, "GetChunkListCommand::" << __func__); proto::WorkerCommandGetChunkListR reply; - reply.set_status(proto::WorkerCommandGetChunkListR::SUCCESS); - + reply.mutable_status(); ChunkInventory::ExistMap const existMap = _chunkInventory->existMap(); - for (auto&& entry : existMap) { string const& db = entry.first; @@ -68,7 +66,6 @@ void GetChunkListCommand::run() { ptr->set_use_count(_resourceMonitor->count(chunk, db)); } } - _frameBuf.serialize(reply); string str(_frameBuf.data(), _frameBuf.size()); _sendChannel->sendStream(xrdsvc::StreamBuffer::createWithMove(str), true); diff --git a/src/wpublish/GetChunkListCommand.h b/src/wpublish/GetChunkListCommand.h index 9676847394..6f4b62ec9c 100644 --- a/src/wpublish/GetChunkListCommand.h +++ b/src/wpublish/GetChunkListCommand.h @@ -59,6 +59,7 @@ class GetChunkListCommand : public wbase::WorkerCommand { std::shared_ptr const& chunkInventory, std::shared_ptr const& resourceMonitor); +protected: void run() override; private: diff --git a/src/wpublish/GetStatusCommand.cc b/src/wpublish/GetStatusCommand.cc index 54ccec4c57..8b790277e0 100644 --- a/src/wpublish/GetStatusCommand.cc +++ b/src/wpublish/GetStatusCommand.cc @@ -62,6 +62,7 @@ void GetStatusCommand::run() { result["filesystem"] = wbase::FileChannelShared::statusToJson(); proto::WorkerCommandGetStatusR reply; + reply.mutable_status(); reply.set_info(result.dump()); _frameBuf.serialize(reply); diff --git a/src/wpublish/GetStatusCommand.h b/src/wpublish/GetStatusCommand.h index 3f7899a8cb..ca4dfebbc7 100644 --- a/src/wpublish/GetStatusCommand.h +++ b/src/wpublish/GetStatusCommand.h @@ -67,6 +67,7 @@ class GetStatusCommand : public wbase::WorkerCommand { virtual ~GetStatusCommand() override = default; +protected: virtual void run() override; private: diff --git a/src/wpublish/RemoveChunkGroupCommand.cc b/src/wpublish/RemoveChunkGroupCommand.cc index 5f54bf6d24..426d96b7e4 100644 --- a/src/wpublish/RemoveChunkGroupCommand.cc +++ b/src/wpublish/RemoveChunkGroupCommand.cc @@ -62,37 +62,23 @@ RemoveChunkGroupCommand::RemoveChunkGroupCommand(shared_ptr _dbs(dbs), _force(force) {} -void RemoveChunkGroupCommand::_reportError(proto::WorkerCommandChunkGroupR::Status status, - string const& message) { - LOGS(_log, LOG_LVL_ERROR, "RemoveChunkGroupCommand::" << __func__ << " " << message); - - proto::WorkerCommandChunkGroupR reply; - - reply.set_status(status); - reply.set_error(message); - - _frameBuf.serialize(reply); - string str(_frameBuf.data(), _frameBuf.size()); - auto streamBuffer = xrdsvc::StreamBuffer::createWithMove(str); - _sendChannel->sendStream(streamBuffer, true); -} - void RemoveChunkGroupCommand::run() { string const context = "RemoveChunkGroupCommand::" + string(__func__) + " "; - LOGS(_log, LOG_LVL_DEBUG, context); - if (not _dbs.size()) { - _reportError(proto::WorkerCommandChunkGroupR::INVALID, - "the list of database names in the group was found empty"); + if (_dbs.empty()) { + reportError( + "the list of database names in the group was found empty", + proto::WorkerCommandStatus::INVALID); return; } // Make sure none of the chunks in the group is not being used // unless in the 'force' mode - if (not _force) { + if (!_force) { if (_resourceMonitor->count(_chunk, _dbs)) { - _reportError(proto::WorkerCommandChunkGroupR::IN_USE, "some chunks of the group are in use"); + reportError("some chunks of the group are in use", + proto::WorkerCommandStatus::IN_USE); return; } } @@ -120,33 +106,28 @@ void RemoveChunkGroupCommand::run() { _chunkInventory->remove(db, _chunk, _mySqlConfig); } catch (InvalidParamError const& ex) { - _reportError(proto::WorkerCommandChunkGroupR::INVALID, ex.what()); + reportError(ex.what(), proto::WorkerCommandStatus::INVALID); return; } catch (QueryError const& ex) { - _reportError(proto::WorkerCommandChunkGroupR::ERROR, ex.what()); + reportError(ex.what()); return; } catch (exception const& ex) { - _reportError(proto::WorkerCommandChunkGroupR::ERROR, - "failed to remove the chunk: " + string(ex.what())); + reportError("failed to remove the chunk: " + string(ex.what())); return; } } - - proto::WorkerCommandChunkGroupR reply; if (_resourceMonitor->count(_chunk, _dbs)) { // Tell a caller that some of the associated resources are still // in use by this worker even though they've been blocked from use for any // further requests. It's up to a caller of this service to correctly // interpret the effect of the operation based on a presence of the "force" // flag in the request. - - reply.set_status(proto::WorkerCommandChunkGroupR::IN_USE); - reply.set_error("some chunks of the group are in use"); - - } else { - reply.set_status(proto::WorkerCommandChunkGroupR::SUCCESS); + reportError("some chunks of the group are in use", + proto::WorkerCommandStatus::IN_USE); + return; } - + proto::WorkerCommandChunkGroupR reply; + reply.mutable_status(); _frameBuf.serialize(reply); string str(_frameBuf.data(), _frameBuf.size()); _sendChannel->sendStream(xrdsvc::StreamBuffer::createWithMove(str), true); diff --git a/src/wpublish/RemoveChunkGroupCommand.h b/src/wpublish/RemoveChunkGroupCommand.h index c724fde246..561713282f 100644 --- a/src/wpublish/RemoveChunkGroupCommand.h +++ b/src/wpublish/RemoveChunkGroupCommand.h @@ -53,7 +53,6 @@ namespace lsst::qserv::wpublish { */ class RemoveChunkGroupCommand : public wbase::WorkerCommand { public: - // The default construction and copy semantics are prohibited RemoveChunkGroupCommand() = delete; RemoveChunkGroupCommand& operator=(const RemoveChunkGroupCommand&) = delete; RemoveChunkGroupCommand(const RemoveChunkGroupCommand&) = delete; @@ -73,20 +72,12 @@ class RemoveChunkGroupCommand : public wbase::WorkerCommand { mysql::MySqlConfig const& mySqlConfig, int chunk, std::vector const& dbs, bool force); - ~RemoveChunkGroupCommand() override = default; + virtual ~RemoveChunkGroupCommand() override = default; - void run() override; +protected: + virtual void run() override; private: - /** - * Report error condition to the logging stream and reply back to - * a service caller. - * - * @param status error status - * @param message message to be reported - */ - void _reportError(proto::WorkerCommandChunkGroupR::Status status, std::string const& message); - // Parameters of the object std::shared_ptr _chunkInventory; diff --git a/src/wpublish/SetChunkListCommand.cc b/src/wpublish/SetChunkListCommand.cc index 84df7b3f22..c918b39713 100644 --- a/src/wpublish/SetChunkListCommand.cc +++ b/src/wpublish/SetChunkListCommand.cc @@ -82,22 +82,6 @@ void SetChunkListCommand::_setChunks(proto::WorkerCommandSetChunkListR& reply, } } -void SetChunkListCommand::_reportError(proto::WorkerCommandSetChunkListR::Status status, - string const& message, ChunkInventory::ExistMap const& prevExistMap) { - LOGS(_log, LOG_LVL_ERROR, "SetChunkListCommand::" << __func__ << " " << message); - - proto::WorkerCommandSetChunkListR reply; - - reply.set_status(status); - reply.set_error(message); - _setChunks(reply, prevExistMap); - - _frameBuf.serialize(reply); - string str(_frameBuf.data(), _frameBuf.size()); - auto streamBuffer = xrdsvc::StreamBuffer::createWithMove(str); - _sendChannel->sendStream(streamBuffer, true); -} - void SetChunkListCommand::run() { string const context = "SetChunkListCommand::" + string(__func__) + " "; @@ -125,9 +109,10 @@ void SetChunkListCommand::run() { // Exclude databases which are not in a scope of this command if (0 == _databases.count(database)) continue; for (auto chunk : entry.second) { - if (_resourceMonitor->count(chunk, database)) { - _reportError(proto::WorkerCommandSetChunkListR::IN_USE, - "some chunks of the group are in use", prevExistMap); + if (_resourceMonitor->count(chunk, database) != 0) { + reportError( + "some chunks of the group are in use", proto::WorkerCommandStatus::IN_USE, + [&](auto& resp) { _setChunks(resp, prevExistMap); }); return; } } @@ -163,14 +148,19 @@ void SetChunkListCommand::run() { clusterManager->Removed(resource.c_str()); } } catch (InvalidParamError const& ex) { - _reportError(proto::WorkerCommandSetChunkListR::INVALID, ex.what(), prevExistMap); + reportError( + ex.what(), proto::WorkerCommandStatus::INVALID, + [&](auto& resp) { _setChunks(resp, prevExistMap); }); return; } catch (QueryError const& ex) { - _reportError(proto::WorkerCommandSetChunkListR::ERROR, ex.what(), prevExistMap); + reportError( + ex.what(), proto::WorkerCommandStatus::ERROR, + [&](auto& resp) { _setChunks(resp, prevExistMap); }); return; } catch (exception const& ex) { - _reportError(proto::WorkerCommandSetChunkListR::ERROR, - "failed to remove the chunk: " + string(ex.what()), prevExistMap); + reportError( + "failed to remove the chunk: " + string(ex.what()), proto::WorkerCommandStatus::ERROR, + [&](auto& resp) { _setChunks(resp, prevExistMap); }); return; } } @@ -198,14 +188,19 @@ void SetChunkListCommand::run() { clusterManager->Added(resource.c_str()); } } catch (InvalidParamError const& ex) { - _reportError(proto::WorkerCommandSetChunkListR::INVALID, ex.what(), prevExistMap); + reportError( + ex.what(), proto::WorkerCommandStatus::INVALID, + [&](auto& resp) { _setChunks(resp, prevExistMap); }); return; } catch (QueryError const& ex) { - _reportError(proto::WorkerCommandSetChunkListR::ERROR, ex.what(), prevExistMap); + reportError( + ex.what(), proto::WorkerCommandStatus::ERROR, + [&](auto& resp) { _setChunks(resp, prevExistMap); }); return; } catch (exception const& ex) { - _reportError(proto::WorkerCommandSetChunkListR::ERROR, - "failed to add the chunk: " + string(ex.what()), prevExistMap); + reportError( + "failed to add the chunk: " + string(ex.what()), proto::WorkerCommandStatus::ERROR, + [&](auto& resp) { _setChunks(resp, prevExistMap); }); return; } } @@ -213,7 +208,7 @@ void SetChunkListCommand::run() { // Send back a reply proto::WorkerCommandSetChunkListR reply; - reply.set_status(proto::WorkerCommandSetChunkListR::SUCCESS); + reply.mutable_status(); _setChunks(reply, prevExistMap); _frameBuf.serialize(reply); diff --git a/src/wpublish/SetChunkListCommand.h b/src/wpublish/SetChunkListCommand.h index 6fa2337d0e..68e3ebe391 100644 --- a/src/wpublish/SetChunkListCommand.h +++ b/src/wpublish/SetChunkListCommand.h @@ -59,10 +59,10 @@ class SetChunkListCommand : public wbase::WorkerCommand { unsigned int chunk; }; - // The default construction and copy semantics are prohibited SetChunkListCommand& operator=(const SetChunkListCommand&) = delete; SetChunkListCommand(const SetChunkListCommand&) = delete; SetChunkListCommand() = delete; + virtual ~SetChunkListCommand() override = default; /** * @param sendChannel communication channel for reporting results @@ -79,30 +79,17 @@ class SetChunkListCommand : public wbase::WorkerCommand { mysql::MySqlConfig const& mySqlConfig, std::vector const& chunks, std::vector const& databases, bool force); - ~SetChunkListCommand() override = default; - - void run() override; +protected: + virtual void run() override; private: /** * Set the chunk list in the reply - * * @param reply message to be initialized * @param prevExistMap previous state of the ChunkList */ void _setChunks(proto::WorkerCommandSetChunkListR& reply, ChunkInventory::ExistMap const& prevExistMap); - /** - * Report error condition to the logging stream and reply back to - * a service caller. - * - * @param status error status - * @param message message to be reported - * @param prevExistMap previous state of the ChunkList - */ - void _reportError(proto::WorkerCommandSetChunkListR::Status status, std::string const& message, - ChunkInventory::ExistMap const& prevExistMap); - // Parameters of the object std::shared_ptr const _chunkInventory; diff --git a/src/wpublish/TestEchoCommand.cc b/src/wpublish/TestEchoCommand.cc index c73aad641b..665209cfbf 100644 --- a/src/wpublish/TestEchoCommand.cc +++ b/src/wpublish/TestEchoCommand.cc @@ -56,7 +56,7 @@ void TestEchoCommand::run() { LOGS(_log, LOG_LVL_DEBUG, "TestEchoCommand::" << __func__); proto::WorkerCommandTestEchoR reply; - reply.set_status(proto::WorkerCommandTestEchoR::SUCCESS); + reply.mutable_status(); reply.set_value(_value); _frameBuf.serialize(reply); diff --git a/src/wpublish/TestEchoCommand.h b/src/wpublish/TestEchoCommand.h index a4e3f1598f..3cbb653a4f 100644 --- a/src/wpublish/TestEchoCommand.h +++ b/src/wpublish/TestEchoCommand.h @@ -56,6 +56,7 @@ class TestEchoCommand : public wbase::WorkerCommand { ~TestEchoCommand() override = default; +protected: void run() override; private: diff --git a/src/xrdreq/ChunkGroupQservRequest.cc b/src/xrdreq/ChunkGroupQservRequest.cc index b5bd2f291d..e72ff5cceb 100644 --- a/src/xrdreq/ChunkGroupQservRequest.cc +++ b/src/xrdreq/ChunkGroupQservRequest.cc @@ -24,7 +24,6 @@ #include "xrdreq/ChunkGroupQservRequest.h" // System headers -#include #include // LSST headers @@ -33,45 +32,11 @@ using namespace std; namespace { - LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.ChunkGroupQservRequest"); - -using namespace lsst::qserv; - -xrdreq::ChunkGroupQservRequest::Status translate(proto::WorkerCommandChunkGroupR::Status status) { - switch (status) { - case proto::WorkerCommandChunkGroupR::SUCCESS: - return xrdreq::ChunkGroupQservRequest::SUCCESS; - case proto::WorkerCommandChunkGroupR::INVALID: - return xrdreq::ChunkGroupQservRequest::INVALID; - case proto::WorkerCommandChunkGroupR::IN_USE: - return xrdreq::ChunkGroupQservRequest::IN_USE; - case proto::WorkerCommandChunkGroupR::ERROR: - return xrdreq::ChunkGroupQservRequest::ERROR; - } - throw domain_error("ChunkGroupQservRequest::" + string(__func__) + " no match for Protobuf status: " + - proto::WorkerCommandChunkGroupR_Status_Name(status)); -} - } // namespace namespace lsst::qserv::xrdreq { -string ChunkGroupQservRequest::status2str(Status status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case INVALID: - return "INVALID"; - case IN_USE: - return "IN_USE"; - case ERROR: - return "ERROR"; - } - throw domain_error("ChunkGroupQservRequest::" + string(__func__) + - " no match for status: " + to_string(status)); -} - ChunkGroupQservRequest::ChunkGroupQservRequest(bool add, unsigned int chunk, vector const& databases, bool force, CallbackType onFinish) : _add(add), _chunk(chunk), _databases(databases), _force(force), _onFinish(onFinish) { @@ -104,7 +69,7 @@ void ChunkGroupQservRequest::onResponse(proto::FrameBufferView& view) { LOGS(_log, LOG_LVL_DEBUG, "ChunkGroupQservRequest[" << (_add ? "add" : "remove") << "** SERVICE REPLY ** status: " - << proto::WorkerCommandChunkGroupR_Status_Name(reply.status())); + << proto::WorkerCommandStatus_Code_Name(reply.status().code())); if (nullptr != _onFinish) { // Clearing the stored callback after finishing the up-stream notification @@ -113,10 +78,9 @@ void ChunkGroupQservRequest::onResponse(proto::FrameBufferView& view) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(::translate(reply.status()), reply.error()); + onFinish(reply.status().code(), reply.status().error()); } } @@ -128,10 +92,9 @@ void ChunkGroupQservRequest::onError(string const& error) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::ERROR, error); + onFinish(proto::WorkerCommandStatus::ERROR, error); } } diff --git a/src/xrdreq/ChunkGroupQservRequest.h b/src/xrdreq/ChunkGroupQservRequest.h index 2927ba3f71..d60b2bac87 100644 --- a/src/xrdreq/ChunkGroupQservRequest.h +++ b/src/xrdreq/ChunkGroupQservRequest.h @@ -30,6 +30,7 @@ #include // Qserv headers +#include "proto/worker.pb.h" #include "xrdreq/QservRequest.h" // This header declarations @@ -41,21 +42,10 @@ namespace lsst::qserv::xrdreq { */ class ChunkGroupQservRequest : public QservRequest { public: - /// Completion status of the operation - enum Status { - SUCCESS, // successful completion of a request - INVALID, // invalid parameters of the request - IN_USE, // request is rejected because one of the chunks is in use - ERROR // an error occurred during command execution - }; - - /// @return string representation of a status - static std::string status2str(Status status); - /// The callback function type to be used for notifications on /// the operation completion. - using CallbackType = std::function; // error message (depends on a status) + using CallbackType = std::function; // error message (if failed) // Default construction and copy semantics is prohibited ChunkGroupQservRequest() = delete; diff --git a/src/xrdreq/ChunkListQservRequest.cc b/src/xrdreq/ChunkListQservRequest.cc index 16914cc567..7d4c641284 100644 --- a/src/xrdreq/ChunkListQservRequest.cc +++ b/src/xrdreq/ChunkListQservRequest.cc @@ -24,43 +24,19 @@ #include "xrdreq/ChunkListQservRequest.h" // System headers -#include #include // Qserv headers #include "lsst/log/Log.h" -using namespace lsst::qserv; using namespace std; namespace { - LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.ChunkListQservRequest"); - -xrdreq::ChunkListQservRequest::Status translate(proto::WorkerCommandUpdateChunkListR::Status status) { - switch (status) { - case proto::WorkerCommandUpdateChunkListR::SUCCESS: - return xrdreq::ChunkListQservRequest::SUCCESS; - case proto::WorkerCommandUpdateChunkListR::ERROR: - return xrdreq::ChunkListQservRequest::ERROR; - } - throw domain_error("ChunkListQservRequest::translate no match for Protobuf status: " + - proto::WorkerCommandUpdateChunkListR_Status_Name(status)); -} } // namespace namespace lsst::qserv::xrdreq { -string ChunkListQservRequest::status2str(Status status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case ERROR: - return "ERROR"; - } - throw domain_error("ChunkListQservRequest::status2str no match for status: " + to_string(status)); -} - ChunkListQservRequest::ChunkListQservRequest(bool rebuild, bool reload, CallbackType onFinish) : _rebuild(rebuild), _reload(reload), _onFinish(onFinish) { LOGS(_log, LOG_LVL_DEBUG, "ChunkListQservRequest ** CONSTRUCTED **"); @@ -89,12 +65,12 @@ void ChunkListQservRequest::onResponse(proto::FrameBufferView& view) { LOGS(_log, LOG_LVL_DEBUG, context << "** SERVICE REPLY ** status: " - << proto::WorkerCommandUpdateChunkListR_Status_Name(reply.status())); + << proto::WorkerCommandStatus_Code_Name(reply.status().code())); ChunkCollection added; ChunkCollection removed; - if (reply.status() == proto::WorkerCommandUpdateChunkListR::SUCCESS) { + if (reply.status().code() == proto::WorkerCommandStatus::SUCCESS) { int const numAdded = reply.added_size(); for (int i = 0; i < numAdded; i++) { proto::WorkerCommandChunk const& chunkEntry = reply.added(i); @@ -118,10 +94,9 @@ void ChunkListQservRequest::onResponse(proto::FrameBufferView& view) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(::translate(reply.status()), reply.error(), added, removed); + onFinish(reply.status().code(), reply.status().error(), added, removed); } } @@ -133,10 +108,9 @@ void ChunkListQservRequest::onError(string const& error) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::ERROR, error, ChunkCollection(), ChunkCollection()); + onFinish(proto::WorkerCommandStatus::ERROR, error, ChunkCollection(), ChunkCollection()); } } diff --git a/src/xrdreq/ChunkListQservRequest.h b/src/xrdreq/ChunkListQservRequest.h index 35f451b342..49cb908a80 100644 --- a/src/xrdreq/ChunkListQservRequest.h +++ b/src/xrdreq/ChunkListQservRequest.h @@ -29,6 +29,7 @@ #include // Qserv headers +#include "proto/worker.pb.h" #include "xrdreq/QservRequest.h" namespace lsst::qserv::xrdreq { @@ -39,15 +40,6 @@ namespace lsst::qserv::xrdreq { */ class ChunkListQservRequest : public QservRequest { public: - /// Completion status of the operation - enum Status { - SUCCESS, // successful completion of a request - ERROR // an error occured during command execution - }; - - /// @return string representation of a status - static std::string status2str(Status status); - /// Struct Chunk a value type encapsulating a chunk number and the name /// of a database struct Chunk { @@ -60,8 +52,8 @@ class ChunkListQservRequest : public QservRequest { /// The callback function type to be used for notifications on /// the operation completion. - using CallbackType = std::function; // chunks removed (if success) diff --git a/src/xrdreq/GetChunkListQservRequest.cc b/src/xrdreq/GetChunkListQservRequest.cc index fb46026f06..686151b980 100644 --- a/src/xrdreq/GetChunkListQservRequest.cc +++ b/src/xrdreq/GetChunkListQservRequest.cc @@ -24,43 +24,19 @@ #include "xrdreq/GetChunkListQservRequest.h" // System headers -#include #include // LSST headers #include "lsst/log/Log.h" -using namespace lsst::qserv; using namespace std; namespace { - LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.GetChunkListQservRequest"); - -xrdreq::GetChunkListQservRequest::Status translate(proto::WorkerCommandGetChunkListR::Status status) { - switch (status) { - case proto::WorkerCommandGetChunkListR::SUCCESS: - return xrdreq::GetChunkListQservRequest::SUCCESS; - case proto::WorkerCommandGetChunkListR::ERROR: - return xrdreq::GetChunkListQservRequest::ERROR; - } - throw domain_error("GetChunkListQservRequest::translate no match for Protobuf status: " + - proto::WorkerCommandGetChunkListR_Status_Name(status)); -} } // namespace namespace lsst::qserv::xrdreq { -string GetChunkListQservRequest::status2str(Status status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case ERROR: - return "ERROR"; - } - throw domain_error("GetChunkListQservRequest::status2str no match for status: " + to_string(status)); -} - GetChunkListQservRequest::Ptr GetChunkListQservRequest::create( bool inUseOnly, GetChunkListQservRequest::CallbackType onFinish) { GetChunkListQservRequest::Ptr ptr(new GetChunkListQservRequest(inUseOnly, onFinish)); @@ -92,11 +68,11 @@ void GetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { LOGS(_log, LOG_LVL_DEBUG, context << "** SERVICE REPLY ** status: " - << proto::WorkerCommandGetChunkListR_Status_Name(reply.status())); + << proto::WorkerCommandStatus_Code_Name(reply.status().code())); ChunkCollection chunks; - if (reply.status() == proto::WorkerCommandGetChunkListR::SUCCESS) { + if (reply.status().code() == proto::WorkerCommandStatus::SUCCESS) { int const num = reply.chunks_size(); for (int i = 0; i < num; i++) { proto::WorkerCommandChunk const& chunkEntry = reply.chunks(i); @@ -113,10 +89,9 @@ void GetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(::translate(reply.status()), reply.error(), chunks); + onFinish(reply.status().code(), reply.status().error(), chunks); } } @@ -128,10 +103,9 @@ void GetChunkListQservRequest::onError(string const& error) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::ERROR, error, ChunkCollection()); + onFinish(proto::WorkerCommandStatus::ERROR, error, ChunkCollection()); } } diff --git a/src/xrdreq/GetChunkListQservRequest.h b/src/xrdreq/GetChunkListQservRequest.h index 20627b9015..b2ac23698b 100644 --- a/src/xrdreq/GetChunkListQservRequest.h +++ b/src/xrdreq/GetChunkListQservRequest.h @@ -29,6 +29,7 @@ #include // Qserv headers +#include "proto/worker.pb.h" #include "xrdreq/QservRequest.h" namespace lsst::qserv::xrdreq { @@ -39,15 +40,6 @@ namespace lsst::qserv::xrdreq { */ class GetChunkListQservRequest : public QservRequest { public: - /// Completion status of the operation - enum Status { - SUCCESS, // successful completion of a request - ERROR // an error occured during command execution - }; - - /// @return string representation of a status - static std::string status2str(Status status); - /// Struct Chunk a value type encapsulating a chunk number and the name /// of a database struct Chunk { @@ -64,8 +56,8 @@ class GetChunkListQservRequest : public QservRequest { /// The callback function type to be used for notifications on /// the operation completion. - using CallbackType = std::function; // chunks (if success) /** diff --git a/src/xrdreq/GetStatusQservRequest.cc b/src/xrdreq/GetStatusQservRequest.cc index 56c1960af6..84f36baa94 100644 --- a/src/xrdreq/GetStatusQservRequest.cc +++ b/src/xrdreq/GetStatusQservRequest.cc @@ -29,24 +29,11 @@ using namespace std; namespace { - LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.GetStatusQservRequest"); - } // namespace namespace lsst::qserv::xrdreq { -string GetStatusQservRequest::status2str(Status status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case ERROR: - return "ERROR"; - } - throw domain_error("GetStatusQservRequest::" + string(__func__) + - " no match for status: " + to_string(status)); -} - GetStatusQservRequest::Ptr GetStatusQservRequest::create(wbase::TaskSelector const& taskSelector, GetStatusQservRequest::CallbackType onFinish) { GetStatusQservRequest::Ptr ptr(new GetStatusQservRequest(taskSelector, onFinish)); @@ -92,10 +79,9 @@ void GetStatusQservRequest::onResponse(proto::FrameBufferView& view) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::SUCCESS, string(), reply.info()); + onFinish(proto::WorkerCommandStatus::SUCCESS, string(), reply.info()); } } @@ -107,10 +93,9 @@ void GetStatusQservRequest::onError(string const& error) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::ERROR, error, string()); + onFinish(proto::WorkerCommandStatus::ERROR, error, string()); } } diff --git a/src/xrdreq/GetStatusQservRequest.h b/src/xrdreq/GetStatusQservRequest.h index d557eb7a83..c15561bad9 100644 --- a/src/xrdreq/GetStatusQservRequest.h +++ b/src/xrdreq/GetStatusQservRequest.h @@ -31,6 +31,7 @@ #include // Qserv headers +#include "proto/worker.pb.h" #include "wbase/TaskState.h" #include "xrdreq/QservRequest.h" @@ -42,22 +43,13 @@ namespace lsst::qserv::xrdreq { */ class GetStatusQservRequest : public QservRequest { public: - /// Completion status of the operation - enum Status { - SUCCESS, // successful completion of a request - ERROR // an error occurred during command execution - }; - - /// @return string representation of a status - static std::string status2str(Status status); - /// The pointer type for instances of the class typedef std::shared_ptr Ptr; /// The callback function type to be used for notifications on /// the operation completion. - using CallbackType = std::function; // worker info received (if success) /** diff --git a/src/xrdreq/QservRequest.h b/src/xrdreq/QservRequest.h index efd57cb670..4306d91311 100644 --- a/src/xrdreq/QservRequest.h +++ b/src/xrdreq/QservRequest.h @@ -45,7 +45,7 @@ class QservRequest : public XrdSsiRequest { public: QservRequest(QservRequest const&) = delete; QservRequest& operator=(QservRequest const&) = delete; - ~QservRequest() override; + virtual ~QservRequest() override; /** * Do a proper request cancellation to ensure a pointer to the request gets deleted diff --git a/src/xrdreq/QueryManagementAction.cc b/src/xrdreq/QueryManagementAction.cc index 759cf3b1dc..9882c74da5 100644 --- a/src/xrdreq/QueryManagementAction.cc +++ b/src/xrdreq/QueryManagementAction.cc @@ -119,9 +119,8 @@ void QueryManagementAction::_notifyAllWorkers(std::string const& xrootdFrontendU // Make and configure the request object auto request = xrdreq::QueryManagementRequest::create( op, queryId, - [self, workerAddress, onFinish](xrdreq::QueryManagementRequest::Status status, - string const& error) { - if (status != xrdreq::QueryManagementRequest::Status::SUCCESS) { + [self, workerAddress, onFinish](proto::WorkerCommandStatus::Code code, string const& error) { + if (code != proto::WorkerCommandStatus::SUCCESS) { self->_response[workerAddress] = error; } if (++(self->_numWorkerRequestsFinished) == self->_response.size()) { diff --git a/src/xrdreq/QueryManagementRequest.cc b/src/xrdreq/QueryManagementRequest.cc index e37a907c20..0122c7cb99 100644 --- a/src/xrdreq/QueryManagementRequest.cc +++ b/src/xrdreq/QueryManagementRequest.cc @@ -23,9 +23,6 @@ // Class header #include "xrdreq/QueryManagementRequest.h" -// System headers -#include - // LSST headers #include "lsst/log/Log.h" @@ -37,17 +34,6 @@ LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.QueryManagementRequest"); namespace lsst::qserv::xrdreq { -string QueryManagementRequest::status2str(Status status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case ERROR: - return "ERROR"; - } - throw domain_error("QueryManagementRequest::" + string(__func__) + - " no match for status: " + to_string(status)); -} - QueryManagementRequest::Ptr QueryManagementRequest::create(proto::QueryManagement::Operation op, QueryId queryId, QueryManagementRequest::CallbackType onFinish) { @@ -81,10 +67,9 @@ void QueryManagementRequest::onResponse(proto::FrameBufferView& view) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::SUCCESS, string()); + onFinish(proto::WorkerCommandStatus::SUCCESS, string()); } } @@ -96,10 +81,9 @@ void QueryManagementRequest::onError(string const& error) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::ERROR, error); + onFinish(proto::WorkerCommandStatus::ERROR, error); } } diff --git a/src/xrdreq/QueryManagementRequest.h b/src/xrdreq/QueryManagementRequest.h index 93471abf96..a66026f62d 100644 --- a/src/xrdreq/QueryManagementRequest.h +++ b/src/xrdreq/QueryManagementRequest.h @@ -43,22 +43,13 @@ namespace lsst::qserv::xrdreq { */ class QueryManagementRequest : public QservRequest { public: - /// Completion status of the operation - enum Status { - SUCCESS, // successful completion of a request - ERROR // an error occurred during command execution - }; - - /// @return string representation of a status - static std::string status2str(Status status); - /// The pointer type for instances of the class typedef std::shared_ptr Ptr; /// The callback function type to be used for notifications on /// the operation completion. - using CallbackType = std::function; // error message + using CallbackType = std::function; // error message (if failed) /** * Static factory method is needed to prevent issues with the lifespan diff --git a/src/xrdreq/SetChunkListQservRequest.cc b/src/xrdreq/SetChunkListQservRequest.cc index 8038b96b06..23ab3226e9 100644 --- a/src/xrdreq/SetChunkListQservRequest.cc +++ b/src/xrdreq/SetChunkListQservRequest.cc @@ -24,51 +24,19 @@ #include "xrdreq/SetChunkListQservRequest.h" // System headers -#include #include // LSST headers #include "lsst/log/Log.h" using namespace std; -using namespace lsst::qserv; namespace { - LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.SetChunkListQservRequest"); - -xrdreq::SetChunkListQservRequest::Status translate(proto::WorkerCommandSetChunkListR::Status status) { - switch (status) { - case proto::WorkerCommandSetChunkListR::SUCCESS: - return xrdreq::SetChunkListQservRequest::SUCCESS; - case proto::WorkerCommandSetChunkListR::INVALID: - return xrdreq::SetChunkListQservRequest::INVALID; - case proto::WorkerCommandSetChunkListR::IN_USE: - return xrdreq::SetChunkListQservRequest::IN_USE; - case proto::WorkerCommandSetChunkListR::ERROR: - return xrdreq::SetChunkListQservRequest::ERROR; - } - throw domain_error("SetChunkListQservRequest::translate no match for Protobuf status: " + - proto::WorkerCommandSetChunkListR_Status_Name(status)); -} } // namespace namespace lsst::qserv::xrdreq { -string SetChunkListQservRequest::status2str(Status status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case INVALID: - return "INVALID"; - case IN_USE: - return "IN_USE"; - case ERROR: - return "ERROR"; - } - throw domain_error("SetChunkListQservRequest::status2str no match for status: " + to_string(status)); -} - SetChunkListQservRequest::Ptr SetChunkListQservRequest::create( SetChunkListQservRequest::ChunkCollection const& chunks, vector const& databases, bool force, SetChunkListQservRequest::CallbackType onFinish) { @@ -114,11 +82,11 @@ void SetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { LOGS(_log, LOG_LVL_DEBUG, context << "** SERVICE REPLY ** status: " - << proto::WorkerCommandSetChunkListR_Status_Name(reply.status())); + << proto::WorkerCommandStatus_Code_Name(reply.status().code())); ChunkCollection chunks; - if (reply.status() == proto::WorkerCommandSetChunkListR::SUCCESS) { + if (reply.status().code() == proto::WorkerCommandStatus::SUCCESS) { int const num = reply.chunks_size(); for (int i = 0; i < num; i++) { proto::WorkerCommandChunk const& chunkEntry = reply.chunks(i); @@ -134,10 +102,9 @@ void SetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(::translate(reply.status()), reply.error(), chunks); + onFinish(reply.status().code(), reply.status().error(), chunks); } } @@ -149,10 +116,9 @@ void SetChunkListQservRequest::onError(string const& error) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::ERROR, error, ChunkCollection()); + onFinish(proto::WorkerCommandStatus::ERROR, error, ChunkCollection()); } } diff --git a/src/xrdreq/SetChunkListQservRequest.h b/src/xrdreq/SetChunkListQservRequest.h index d43f2c0f96..4cf0d6e09d 100644 --- a/src/xrdreq/SetChunkListQservRequest.h +++ b/src/xrdreq/SetChunkListQservRequest.h @@ -30,6 +30,7 @@ #include // Qserv headers +#include "proto/worker.pb.h" #include "xrdreq/QservRequest.h" namespace lsst::qserv::xrdreq { @@ -40,17 +41,6 @@ namespace lsst::qserv::xrdreq { */ class SetChunkListQservRequest : public QservRequest { public: - /// Completion status of the operation - enum Status { - SUCCESS, // successful completion of a request - INVALID, // invalid parameters of the request - IN_USE, // request is rejected because one of the chunks is in use - ERROR // an error occurred during command execution - }; - - /// @return string representation of a status - static std::string status2str(Status status); - /// Struct Chunk a value type encapsulating a chunk number and the name /// of a database struct Chunk { @@ -67,8 +57,8 @@ class SetChunkListQservRequest : public QservRequest { /// The callback function type to be used for notifications on /// the operation completion. - using CallbackType = std::function; // chunks (if success) /** diff --git a/src/xrdreq/TestEchoQservRequest.cc b/src/xrdreq/TestEchoQservRequest.cc index ad9bda0342..ed17a5dd04 100644 --- a/src/xrdreq/TestEchoQservRequest.cc +++ b/src/xrdreq/TestEchoQservRequest.cc @@ -24,7 +24,6 @@ #include "xrdreq/TestEchoQservRequest.h" // System headers -#include #include // LSST headers @@ -33,36 +32,11 @@ using namespace std; namespace { - LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.TestEchoQservRequest"); - -using namespace lsst::qserv; - -xrdreq::TestEchoQservRequest::Status translate(proto::WorkerCommandTestEchoR::Status status) { - switch (status) { - case proto::WorkerCommandTestEchoR::SUCCESS: - return xrdreq::TestEchoQservRequest::SUCCESS; - case proto::WorkerCommandTestEchoR::ERROR: - return xrdreq::TestEchoQservRequest::ERROR; - } - throw domain_error("TestEchoQservRequest::" + string(__func__) + " no match for Protobuf status: " + - proto::WorkerCommandTestEchoR_Status_Name(status)); -} } // namespace namespace lsst::qserv::xrdreq { -string TestEchoQservRequest::status2str(Status status) { - switch (status) { - case SUCCESS: - return "SUCCESS"; - case ERROR: - return "ERROR"; - } - throw domain_error("TestEchoQservRequest::" + string(__func__) + - " no match for status: " + to_string(status)); -} - TestEchoQservRequest::Ptr TestEchoQservRequest::create(string const& value, TestEchoQservRequest::CallbackType onFinish) { TestEchoQservRequest::Ptr ptr(new TestEchoQservRequest(value, onFinish)); @@ -95,7 +69,7 @@ void TestEchoQservRequest::onResponse(proto::FrameBufferView& view) { LOGS(_log, LOG_LVL_DEBUG, "TestEchoQservRequest ** SERVICE REPLY ** status: " - << proto::WorkerCommandTestEchoR_Status_Name(reply.status())); + << proto::WorkerCommandStatus_Code_Name(reply.status().code())); if (nullptr != _onFinish) { // Clearing the stored callback after finishing the up-stream notification @@ -104,10 +78,9 @@ void TestEchoQservRequest::onResponse(proto::FrameBufferView& view) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(::translate(reply.status()), reply.error(), _value, reply.value()); + onFinish(reply.status().code(), reply.status().error(), _value, reply.value()); } } @@ -119,10 +92,9 @@ void TestEchoQservRequest::onError(string const& error) { // 1. it guaranties (exactly) one time notification // 2. it breaks the up-stream dependency on a caller object if a shared // pointer to the object was mentioned as the lambda-function's closure - auto onFinish = move(_onFinish); _onFinish = nullptr; - onFinish(Status::ERROR, error, _value, string()); + onFinish(proto::WorkerCommandStatus::ERROR, error, _value, string()); } } diff --git a/src/xrdreq/TestEchoQservRequest.h b/src/xrdreq/TestEchoQservRequest.h index 2a375e408d..0899799894 100644 --- a/src/xrdreq/TestEchoQservRequest.h +++ b/src/xrdreq/TestEchoQservRequest.h @@ -29,6 +29,7 @@ #include // Qserv headers +#include "proto/worker.pb.h" #include "xrdreq/QservRequest.h" namespace lsst::qserv::xrdreq { @@ -39,22 +40,13 @@ namespace lsst::qserv::xrdreq { */ class TestEchoQservRequest : public QservRequest { public: - /// Completion status of the operation - enum Status { - SUCCESS, // successful completion of a request - ERROR // an error occurred during command execution - }; - - /// @return string representation of a status - static std::string status2str(Status status); - /// The pointer type for instances of the class typedef std::shared_ptr Ptr; /// The callback function type to be used for notifications on /// the operation completion. - using CallbackType = std::function; // value received (if success) diff --git a/src/xrdreq/qserv-query-management.cc b/src/xrdreq/qserv-query-management.cc index 65a7b87757..dc7a5eda59 100644 --- a/src/xrdreq/qserv-query-management.cc +++ b/src/xrdreq/qserv-query-management.cc @@ -93,10 +93,9 @@ int test() { // Prepare the request auto request = xrdreq::QueryManagementRequest::create( - operation, queryId, - [&finished](xrdreq::QueryManagementRequest::Status status, string const& error) { - cout << "status=" << xrdreq::QueryManagementRequest::status2str(status) << ", error='" - << error << "'" << endl; + operation, queryId, [&finished](proto::WorkerCommandStatus::Code code, string const& error) { + cout << "code=" << proto::WorkerCommandStatus_Code_Name(code) << ", error='" << error + << "'" << endl; finished = true; }); diff --git a/src/xrdreq/qserv-worker-notify.cc b/src/xrdreq/qserv-worker-notify.cc index e82e135340..c096085ce4 100644 --- a/src/xrdreq/qserv-worker-notify.cc +++ b/src/xrdreq/qserv-worker-notify.cc @@ -28,6 +28,7 @@ extern XrdSsiProvider* XrdSsiProviderClient; namespace global = lsst::qserv; +namespace proto = lsst::qserv::proto; namespace util = lsst::qserv::util; namespace xrdreq = lsst::qserv::xrdreq; @@ -109,11 +110,11 @@ int test() { if ("GET_CHUNK_LIST" == operation) { request = xrdreq::GetChunkListQservRequest::create( - inUseOnly, [&finished](xrdreq::GetChunkListQservRequest::Status status, string const& error, + inUseOnly, [&finished](proto::WorkerCommandStatus::Code code, string const& error, xrdreq::GetChunkListQservRequest::ChunkCollection const& chunks) { - if (status != xrdreq::GetChunkListQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::GetChunkListQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "# total chunks: " << chunks.size() << "\n" << endl; if (chunks.size()) { @@ -137,11 +138,11 @@ int test() { request = xrdreq::SetChunkListQservRequest::create( chunks, databases, force, - [&finished](xrdreq::SetChunkListQservRequest::Status status, string const& error, + [&finished](proto::WorkerCommandStatus::Code code, string const& error, xrdreq::SetChunkListQservRequest::ChunkCollection const& chunks) { - if (status != xrdreq::SetChunkListQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::SetChunkListQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "# total chunks: " << chunks.size() << "\n" << endl; if (chunks.size()) { @@ -160,12 +161,12 @@ int test() { } else if ("REBUILD_CHUNK_LIST" == operation) { request = xrdreq::RebuildChunkListQservRequest::create( - reload, [&finished](xrdreq::ChunkListQservRequest::Status status, string const& error, + reload, [&finished](proto::WorkerCommandStatus::Code code, string const& error, xrdreq::ChunkListQservRequest::ChunkCollection const& added, xrdreq::ChunkListQservRequest::ChunkCollection const& removed) { - if (status != xrdreq::ChunkListQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::ChunkListQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "# chunks added: " << added.size() << "\n" << "# chuks removed: " << removed.size() << endl; @@ -175,12 +176,12 @@ int test() { } else if ("RELOAD_CHUNK_LIST" == operation) { request = xrdreq::ReloadChunkListQservRequest::create( - [&finished](xrdreq::ChunkListQservRequest::Status status, string const& error, + [&finished](proto::WorkerCommandStatus::Code code, string const& error, xrdreq::ChunkListQservRequest::ChunkCollection const& added, xrdreq::ChunkListQservRequest::ChunkCollection const& removed) { - if (status != xrdreq::ChunkListQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::ChunkListQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "# chunks added: " << added.size() << "\n" << "# chuks removed: " << removed.size() << endl; @@ -190,32 +191,31 @@ int test() { } else if ("ADD_CHUNK_GROUP" == operation) { request = xrdreq::AddChunkGroupQservRequest::create( - chunk, dbs, [&finished](xrdreq::ChunkGroupQservRequest::Status status, string const& error) { - if (status != xrdreq::ChunkGroupQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::ChunkGroupQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + chunk, dbs, [&finished](proto::WorkerCommandStatus::Code code, string const& error) { + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } finished = true; }); } else if ("REMOVE_CHUNK_GROUP" == operation) { request = xrdreq::RemoveChunkGroupQservRequest::create( - chunk, dbs, force, - [&finished](xrdreq::ChunkGroupQservRequest::Status status, string const& error) { - if (status != xrdreq::ChunkGroupQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::ChunkGroupQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + chunk, dbs, force, [&finished](proto::WorkerCommandStatus::Code code, string const& error) { + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } finished = true; }); } else if ("TEST_ECHO" == operation) { request = xrdreq::TestEchoQservRequest::create( - value, [&finished](xrdreq::TestEchoQservRequest::Status status, string const& error, + value, [&finished](proto::WorkerCommandStatus::Code code, string const& error, string const& sent, string const& received) { - if (status != xrdreq::TestEchoQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::TestEchoQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "value sent: " << sent << "\n" << "value received: " << received << endl; @@ -226,11 +226,10 @@ int test() { } else if ("GET_STATUS" == operation) { request = xrdreq::GetStatusQservRequest::create( includeTasks, queryIds, - [&finished](xrdreq::GetStatusQservRequest::Status status, string const& error, - string const& info) { - if (status != xrdreq::GetStatusQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::GetStatusQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + [&finished](proto::WorkerCommandStatus::Code code, string const& error, string const& info) { + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "worker info: " << info << endl; } diff --git a/src/xrdreq/qserv-worker-perf-chunks.cc b/src/xrdreq/qserv-worker-perf-chunks.cc index 78efb09bf1..a48ffd46ec 100644 --- a/src/xrdreq/qserv-worker-perf-chunks.cc +++ b/src/xrdreq/qserv-worker-perf-chunks.cc @@ -54,14 +54,13 @@ extern XrdSsiProvider* XrdSsiProviderClient; namespace global = lsst::qserv; +namespace proto = lsst::qserv::proto; namespace util = lsst::qserv::util; namespace xrdreq = lsst::qserv::xrdreq; using namespace std; using namespace std::placeholders; -using RequestT = xrdreq::TestEchoQservRequest; - namespace { // Command line parameters @@ -131,7 +130,8 @@ class Counter { class RequestManager : public enable_shared_from_this { public: typedef shared_ptr Ptr; - typedef function OnFinish; + typedef function + OnFinish; static Ptr create(Counter::Ptr const& numRequestsInFlight, XrdSsiService* serviceProvider, string const& resourcePath, string const& payload, OnFinish const& onFinish, @@ -144,16 +144,17 @@ class RequestManager : public enable_shared_from_this { RequestManager& operator=(RequestManager const&) = delete; void start() { - _ptr = RequestT::create(_payload, - bind(&RequestManager::_finished, shared_from_this(), _1, _2, _3, _4)); + _ptr = xrdreq::TestEchoQservRequest::create( + _payload, bind(&RequestManager::_finished, shared_from_this(), _1, _2, _3, _4)); _numRequestsInFlight->inc(); XrdSsiResource resource(_resourcePath); _serviceProvider->ProcessRequest(*_ptr, resource); } private: - void _finished(RequestT::Status status, string const& error, string const& sent, string const& received) { - _onFinish(status, error, sent, received); + void _finished(proto::WorkerCommandStatus::Code code, string const& error, string const& sent, + string const& received) { + _onFinish(code, error, sent, received); _numRequestsInFlight->dec(); if (_memoryCleanup) _ptr = nullptr; } @@ -174,7 +175,7 @@ class RequestManager : public enable_shared_from_this { string const _payload; OnFinish const _onFinish; bool const _memoryCleanup; - RequestT::Ptr _ptr; + xrdreq::TestEchoQservRequest::Ptr _ptr; }; int test() { @@ -245,11 +246,11 @@ int test() { for (auto&& resourcePath : jobs) { auto const ptr = RequestManager::create( numRequestsInFlight, serviceProvider, resourcePath, payload, - [](RequestT::Status status, string const& error, string const& sent, - string const& received) { + [](proto::WorkerCommandStatus::Code code, string const& error, + string const& sent, string const& received) { if (not silent) { - if (status != RequestT::Status::SUCCESS) { - cout << "status: " << RequestT::status2str(status) + if (status != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << ", error: " << error << endl; } else { cout << "value sent: '" << sent << "', received: '" << received diff --git a/src/xrdreq/qserv-worker-perf.cc b/src/xrdreq/qserv-worker-perf.cc index 03d6b633c8..282f638a2b 100644 --- a/src/xrdreq/qserv-worker-perf.cc +++ b/src/xrdreq/qserv-worker-perf.cc @@ -22,6 +22,7 @@ extern XrdSsiProvider* XrdSsiProviderClient; namespace global = lsst::qserv; +namespace proto = lsst::qserv::proto; namespace util = lsst::qserv::util; namespace xrdreq = lsst::qserv::xrdreq; @@ -67,11 +68,11 @@ int test() { for (unsigned int i = 0; i < numRequests; ++i) { auto request = xrdreq::TestEchoQservRequest::create( - value, [&finished](xrdreq::TestEchoQservRequest::Status status, string const& error, + value, [&finished](proto::WorkerCommandStatus::Code code, string const& error, string const& sent, string const& received) { - if (status != xrdreq::TestEchoQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::TestEchoQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "value sent: " << sent << "\n" << "value received: " << received << endl; @@ -92,11 +93,11 @@ int test() { for (unsigned int j = 0; j < numWorkers; ++j) { string const& worker = workers[j]; auto request = xrdreq::TestEchoQservRequest::create( - value, [&finished](xrdreq::TestEchoQservRequest::Status status, string const& error, + value, [&finished](proto::WorkerCommandStatus::Code code, string const& error, string const& sent, string const& received) { - if (status != xrdreq::TestEchoQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::TestEchoQservRequest::status2str(status) << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatus::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { cout << "value sent: " << sent << "\n" << "value received: " << received << endl; diff --git a/src/xrdreq/qserv-worker-status.cc b/src/xrdreq/qserv-worker-status.cc index 4e16a43d22..d84e1387d8 100644 --- a/src/xrdreq/qserv-worker-status.cc +++ b/src/xrdreq/qserv-worker-status.cc @@ -22,6 +22,7 @@ extern XrdSsiProvider* XrdSsiProviderClient; namespace global = lsst::qserv; +namespace proto = lsst::qserv::proto; namespace util = lsst::qserv::util; namespace xrdreq = lsst::qserv::xrdreq; @@ -69,14 +70,13 @@ int test() { for (unsigned int i = 0; i < numRequests; ++i) { auto request = xrdreq::GetStatusQservRequest::create( includeTasks, queryIds, - [&finished](xrdreq::GetStatusQservRequest::Status status, string const& error, + [&finished](proto::WorkerCommandStatus::Code code, string const& error, string const& info) { - if (status != xrdreq::GetStatusQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::GetStatusQservRequest::status2str(status) - << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatu::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { - cout << "info: " << info << endl; + cout << "info: " << info << endl; } finished--; }); @@ -94,14 +94,13 @@ int test() { for (unsigned int j = 0; j < numWorkers; ++j) { string const& worker = workers[j]; auto request = xrdreq::GetStatusQservRequest::create( - [&finished](xrdreq::GetStatusQservRequest::Status status, string const& error, + [&finished](proto::WorkerCommandStatus::Code code, string const& error, string const& info) { - if (status != xrdreq::GetStatusQservRequest::Status::SUCCESS) { - cout << "status: " << xrdreq::GetStatusQservRequest::status2str(status) - << "\n" - << "error: " << error << endl; + if (code != proto::WorkerCommandStatu::SUCCESS) { + cout << "code: " << proto::WorkerCommandStatus_Code_Name(code) << "\n" + << "error: " << error << endl; } else { - cout << "info: " << info << endl; + cout << "info: " << info << endl; } finished--; }); From 20293e60f1391d84fbb26cc0aa97d9af5ab1f3d8 Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Tue, 1 Aug 2023 03:24:19 +0000 Subject: [PATCH 03/12] Extended Replication Framework to pull status of worker databases --- src/proto/worker.proto | 22 ++-- src/replica/CMakeLists.txt | 2 + src/replica/GetDbStatusQservMgtRequest.cc | 128 ++++++++++++++++++++++ src/replica/GetDbStatusQservMgtRequest.h | 115 +++++++++++++++++++ src/replica/QservMgtServices.cc | 34 ++++++ src/replica/QservMgtServices.h | 18 +++ src/wpublish/CMakeLists.txt | 1 + src/wpublish/GetDbStatusCommand.cc | 79 +++++++++++++ src/wpublish/GetDbStatusCommand.h | 62 +++++++++++ src/xrdreq/CMakeLists.txt | 1 + src/xrdreq/GetDbStatusQservRequest.cc | 93 ++++++++++++++++ src/xrdreq/GetDbStatusQservRequest.h | 87 +++++++++++++++ src/xrdsvc/SsiRequest.cc | 5 + 13 files changed, 640 insertions(+), 7 deletions(-) create mode 100644 src/replica/GetDbStatusQservMgtRequest.cc create mode 100644 src/replica/GetDbStatusQservMgtRequest.h create mode 100644 src/wpublish/GetDbStatusCommand.cc create mode 100644 src/wpublish/GetDbStatusCommand.h create mode 100644 src/xrdreq/GetDbStatusQservRequest.cc create mode 100644 src/xrdreq/GetDbStatusQservRequest.h diff --git a/src/proto/worker.proto b/src/proto/worker.proto index 548591bc51..ee6049b0c8 100644 --- a/src/proto/worker.proto +++ b/src/proto/worker.proto @@ -150,13 +150,14 @@ message Result { // separate messages (of the corresponding types). message WorkerCommandH { enum Command { - TEST_ECHO = 1; // Return back a value sent to the command processor. - ADD_CHUNK_GROUP = 2; // Add a group of collocated chunks. - REMOVE_CHUNK_GROUP = 3; // Remove a group of collocated chunks. - UPDATE_CHUNK_LIST = 4; // Update (rebuild and/or reload) the list of available chunks. - GET_CHUNK_LIST = 5; // Return a list of chunks known to a worker. - SET_CHUNK_LIST = 6; // Set a new list of chunks. - GET_STATUS = 7; // Return various status info on a worker. + TEST_ECHO = 1; // Return back a value sent to the command processor. + ADD_CHUNK_GROUP = 2; // Add a group of collocated chunks. + REMOVE_CHUNK_GROUP = 3; // Remove a group of collocated chunks. + UPDATE_CHUNK_LIST = 4; // Update (rebuild and/or reload) the list of available chunks. + GET_CHUNK_LIST = 5; // Return a list of chunks known to a worker. + SET_CHUNK_LIST = 6; // Set a new list of chunks. + GET_STATUS = 7; // Return various status info on a worker. + GET_DATABASE_STATUS = 8; // Return various info on the worker database service. } required Command command = 1; } @@ -274,6 +275,13 @@ message WorkerCommandGetStatusR { required string info = 1; // Status info serialized from a JSON object } +// The message to be sent back in response to the 'GET_DATABASE_STATUS' command +// +message WorkerCommandGetDbStatusR { + required WorkerCommandStatus status = 1; // Completion status of the operation + required string info = 2; // Status info serialized from a JSON object +} + ///////////////////////////////////////////////////////////////// // Protocol definition for the query management requests. These // requests do not require any response messages to be explicitly diff --git a/src/replica/CMakeLists.txt b/src/replica/CMakeLists.txt index cd9163d188..28e8855cf1 100644 --- a/src/replica/CMakeLists.txt +++ b/src/replica/CMakeLists.txt @@ -144,6 +144,8 @@ target_sources(replica PRIVATE FixUpJob.h GetReplicasQservMgtRequest.cc GetReplicasQservMgtRequest.h + GetDbStatusQservMgtRequest.cc + GetDbStatusQservMgtRequest.h GetStatusQservMgtRequest.cc GetStatusQservMgtRequest.h HealthMonitorTask.cc diff --git a/src/replica/GetDbStatusQservMgtRequest.cc b/src/replica/GetDbStatusQservMgtRequest.cc new file mode 100644 index 0000000000..c1e89bd526 --- /dev/null +++ b/src/replica/GetDbStatusQservMgtRequest.cc @@ -0,0 +1,128 @@ +/* + * LSST Data Management System + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ + +// Class header +#include "replica/GetDbStatusQservMgtRequest.h" + +// System headers +#include +#include + +// Third party headers +#include "XrdSsi/XrdSsiProvider.hh" +#include "XrdSsi/XrdSsiService.hh" + +// Qserv headers +#include "global/ResourceUnit.h" +#include "proto/worker.pb.h" +#include "replica/ServiceProvider.h" + +// LSST headers +#include "lsst/log/Log.h" + +using namespace nlohmann; +using namespace std; + +namespace { + +LOG_LOGGER _log = LOG_GET("lsst.qserv.replica.GetDbStatusQservMgtRequest"); + +} // namespace + +namespace lsst::qserv::replica { + +GetDbStatusQservMgtRequest::Ptr GetDbStatusQservMgtRequest::create( + ServiceProvider::Ptr const& serviceProvider, string const& worker, + GetDbStatusQservMgtRequest::CallbackType const& onFinish) { + return GetDbStatusQservMgtRequest::Ptr(new GetDbStatusQservMgtRequest(serviceProvider, worker, onFinish)); +} + +GetDbStatusQservMgtRequest::GetDbStatusQservMgtRequest( + ServiceProvider::Ptr const& serviceProvider, string const& worker, + GetDbStatusQservMgtRequest::CallbackType const& onFinish) + : QservMgtRequest(serviceProvider, "QSERV_GET_DATABASE_STATUS", worker), _onFinish(onFinish) {} + +json const& GetDbStatusQservMgtRequest::info() const { + if (!((state() == State::FINISHED) && (extendedState() == ExtendedState::SUCCESS))) { + throw logic_error("GetDbStatusQservMgtRequest::" + string(__func__) + + " no info available in state: " + state2string(state(), extendedState())); + } + return _info; +} + +list> GetDbStatusQservMgtRequest::extendedPersistentState() const { + list> result; + return result; +} + +void GetDbStatusQservMgtRequest::startImpl(replica::Lock const& lock) { + auto const request = shared_from_base(); + _qservRequest = xrdreq::GetDbStatusQservRequest::create([request](proto::WorkerCommandStatus::Code code, + string const& error, + string const& info) { + if (request->state() == State::FINISHED) return; + replica::Lock const lock(request->_mtx, request->context() + string(__func__) + "[callback]"); + if (request->state() == State::FINISHED) return; + + switch (code) { + case proto::WorkerCommandStatus::SUCCESS: + try { + request->_setInfo(lock, info); + request->finish(lock, QservMgtRequest::ExtendedState::SUCCESS); + } catch (exception const& ex) { + string const msg = "failed to parse worker response, ex: " + string(ex.what()); + LOGS(_log, LOG_LVL_ERROR, "GetDbStatusQservMgtRequest::" << __func__ << " " << msg); + request->finish(lock, QservMgtRequest::ExtendedState::SERVER_BAD_RESPONSE, msg); + } + break; + case proto::WorkerCommandStatus::ERROR: + request->finish(lock, QservMgtRequest::ExtendedState::SERVER_ERROR, error); + break; + default: + throw logic_error("GetDbStatusQservMgtRequest::" + string(__func__) + + " unhandled server status: " + proto::WorkerCommandStatus_Code_Name(code)); + } + }); + XrdSsiResource resource(ResourceUnit::makeWorkerPath(worker())); + service()->ProcessRequest(*_qservRequest, resource); +} + +void GetDbStatusQservMgtRequest::finishImpl(replica::Lock const& lock) { + switch (extendedState()) { + case ExtendedState::CANCELLED: + case ExtendedState::TIMEOUT_EXPIRED: + if (_qservRequest) _qservRequest->cancel(); + break; + default: + break; + } +} + +void GetDbStatusQservMgtRequest::notify(replica::Lock const& lock) { + LOGS(_log, LOG_LVL_TRACE, context() << __func__); + notifyDefaultImpl(lock, _onFinish); +} + +void GetDbStatusQservMgtRequest::_setInfo(replica::Lock const& lock, string const& info) { + _info = json::parse(info); +} + +} // namespace lsst::qserv::replica diff --git a/src/replica/GetDbStatusQservMgtRequest.h b/src/replica/GetDbStatusQservMgtRequest.h new file mode 100644 index 0000000000..57a7d8b42b --- /dev/null +++ b/src/replica/GetDbStatusQservMgtRequest.h @@ -0,0 +1,115 @@ +/* + * LSST Data Management System + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ +#ifndef LSST_QSERV_REPLICA_GETDBSTATUSQSERVMGTREQUEST_H +#define LSST_QSERV_REPLICA_GETDBSTATUSQSERVMGTREQUEST_H + +// System headers +#include +#include + +// Third party headers +#include "nlohmann/json.hpp" + +// Qserv headers +#include "replica/QservMgtRequest.h" +#include "replica/ServiceProvider.h" +#include "xrdreq/GetDbStatusQservRequest.h" + +// This header declarations +namespace lsst::qserv::replica { + +/** + * Class GetDbStatusQservMgtRequest is a request for obtaining various info + * on the database service of the Qserv worker. + */ +class GetDbStatusQservMgtRequest : public QservMgtRequest { +public: + typedef std::shared_ptr Ptr; + + /// The function type for notifications on the completion of the request + typedef std::function CallbackType; + + GetDbStatusQservMgtRequest() = delete; + GetDbStatusQservMgtRequest(GetDbStatusQservMgtRequest const&) = delete; + GetDbStatusQservMgtRequest& operator=(GetDbStatusQservMgtRequest const&) = delete; + + ~GetDbStatusQservMgtRequest() final = default; + + /** + * Static factory method is needed to prevent issues with the lifespan + * and memory management of instances created otherwise (as values or via + * low-level pointers). + * @param serviceProvider A reference to a provider of services for accessing + * Configuration, saving the request's persistent state to the database. + * @param worker The name of a worker to send the request to. + * @param onFinish (optional) callback function to be called upon request completion. + * @return A pointer to the created object. + */ + static Ptr create(ServiceProvider::Ptr const& serviceProvider, std::string const& worker, + CallbackType const& onFinish = nullptr); + + /** + * @return The info object returned back by the worker. + * @note The method will throw exception std::logic_error if called before + * the request finishes or if it's finished with any status but SUCCESS. + */ + nlohmann::json const& info() const; + + /// @see QservMgtRequest::extendedPersistentState() + std::list> extendedPersistentState() const override; + +protected: + /// @see QservMgtRequest::startImpl() + void startImpl(replica::Lock const& lock) final; + + /// @see QservMgtRequest::finishImpl() + void finishImpl(replica::Lock const& lock) final; + + /// @see QservMgtRequest::notify() + void notify(replica::Lock const& lock) final; + +private: + /// @see GetDbStatusQservMgtRequest::create() + GetDbStatusQservMgtRequest(ServiceProvider::Ptr const& serviceProvider, std::string const& worker, + CallbackType const& onFinish); + + /** + * Carry over results of the request into a local storage. + * @param lock A lock on QservMgtRequest::_mtx must be acquired by a caller of the method. + * @param info The data string returned by a worker. + */ + void _setInfo(replica::Lock const& lock, std::string const& info); + + // Input parameters + + std::string const _data; + CallbackType _onFinish; ///< this object is reset after finishing the request + + /// A request to the remote services + xrdreq::GetDbStatusQservRequest::Ptr _qservRequest; + + /// The info object returned by the Qserv worker + nlohmann::json _info; +}; + +} // namespace lsst::qserv::replica + +#endif // LSST_QSERV_REPLICA_GETDBSTATUSQSERVMGTREQUEST_H diff --git a/src/replica/QservMgtServices.cc b/src/replica/QservMgtServices.cc index 2e363d5d0b..7422b6910a 100644 --- a/src/replica/QservMgtServices.cc +++ b/src/replica/QservMgtServices.cc @@ -312,6 +312,40 @@ GetStatusQservMgtRequest::Ptr QservMgtServices::status(std::string const& worker return request; } +GetDbStatusQservMgtRequest::Ptr QservMgtServices::databaseStatus( + std::string const& worker, std::string const& jobId, + GetDbStatusQservMgtRequest::CallbackType const& onFinish, unsigned int requestExpirationIvalSec) { + GetDbStatusQservMgtRequest::Ptr request; + + // Make sure the XROOTD/SSI service is available before attempting + // any operations on requests + + XrdSsiService* service = _xrdSsiService(); + if (not service) { + return request; + } else { + replica::Lock lock(_mtx, "QservMgtServices::" + string(__func__)); + + auto const manager = shared_from_this(); + + request = GetDbStatusQservMgtRequest::create( + serviceProvider(), worker, + [manager](QservMgtRequest::Ptr const& request) { manager->_finish(request->id()); }); + + // Register the request (along with its callback) by its unique + // identifier in the local registry. Once it's complete it'll + // be automatically removed from the Registry. + _registry[request->id()] = + make_shared>(request, onFinish); + } + + // Initiate the request in the lock-free zone to avoid blocking the service + // from initiating other requests which this one is starting. + request->start(service, jobId, requestExpirationIvalSec); + + return request; +} + void QservMgtServices::_finish(string const& id) { string const context = id + " QservMgtServices::" + string(__func__) + " "; diff --git a/src/replica/QservMgtServices.h b/src/replica/QservMgtServices.h index eb1b973f14..1b556730d0 100644 --- a/src/replica/QservMgtServices.h +++ b/src/replica/QservMgtServices.h @@ -30,6 +30,7 @@ // Qserv headers #include "replica/AddReplicaQservMgtRequest.h" #include "replica/GetReplicasQservMgtRequest.h" +#include "replica/GetDbStatusQservMgtRequest.h" #include "replica/GetStatusQservMgtRequest.h" #include "replica/RemoveReplicaQservMgtRequest.h" #include "replica/ServiceProvider.h" @@ -218,6 +219,23 @@ class QservMgtServices : public std::enable_shared_from_this { GetStatusQservMgtRequest::CallbackType const& onFinish = nullptr, unsigned int requestExpirationIvalSec = 0); + /** + * Request detailed status on the database service of a Qserv worker + * + * @param worker The name of a worker. + * @param jobId An optional identifier of a job specifying a context in which + * a request will be executed. + * @param onFinish A callback function to be called upon request completion. + * @param requestExpirationIvalSec An optional parameter (if differs from 0) allowing it + * to override the default value of the corresponding parameter from the Configuration. + * @return A pointer to the request object if the request was made. Return + * nullptr otherwise. + */ + GetDbStatusQservMgtRequest::Ptr databaseStatus( + std::string const& worker, std::string const& jobId = "", + GetDbStatusQservMgtRequest::CallbackType const& onFinish = nullptr, + unsigned int requestExpirationIvalSec = 0); + private: /** * @param serviceProvider Is required for accessing configuration parameters. diff --git a/src/wpublish/CMakeLists.txt b/src/wpublish/CMakeLists.txt index 985020656e..2bce3cc790 100644 --- a/src/wpublish/CMakeLists.txt +++ b/src/wpublish/CMakeLists.txt @@ -6,6 +6,7 @@ target_sources(wpublish PRIVATE ChunkInventory.cc ChunkListCommand.cc GetChunkListCommand.cc + GetDbStatusCommand.cc GetStatusCommand.cc QueriesAndChunks.cc RemoveChunkGroupCommand.cc diff --git a/src/wpublish/GetDbStatusCommand.cc b/src/wpublish/GetDbStatusCommand.cc new file mode 100644 index 0000000000..58276467de --- /dev/null +++ b/src/wpublish/GetDbStatusCommand.cc @@ -0,0 +1,79 @@ +// -*- LSST-C++ -*- +/* + * LSST Data Management System + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ + +// Class header +#include "wpublish/GetDbStatusCommand.h" + +// System headers +#include + +// Third party headers +#include "nlohmann/json.hpp" + +// Qserv headers +#include "mysql/MySqlUtils.h" +#include "proto/worker.pb.h" +#include "wbase/SendChannel.h" +#include "wconfig/WorkerConfig.h" + +// LSST headers +#include "lsst/log/Log.h" + +using namespace std; +using json = nlohmann::json; + +namespace { + +LOG_LOGGER _log = LOG_GET("lsst.qserv.wpublish.GetDbStatusCommand"); + +} // anonymous namespace + +namespace lsst::qserv::wpublish { + +GetDbStatusCommand::GetDbStatusCommand(shared_ptr const& sendChannel) + : wbase::WorkerCommand(sendChannel) {} + +void GetDbStatusCommand::run() { + string const context = "GetDbStatusCommand::" + string(__func__); + LOGS(_log, LOG_LVL_DEBUG, context); + + json result; + try { + bool const full = true; + result = mysql::MySqlUtils::processList(wconfig::WorkerConfig::instance()->getMySqlConfig(), full); + } catch (mysql::MySqlQueryError const& ex) { + LOGS(_log, LOG_LVL_ERROR, context << " " << ex.what()); + reportError(ex.what()); + return; + } + proto::WorkerCommandGetDbStatusR reply; + reply.mutable_status(); + reply.set_info(result.dump()); + + _frameBuf.serialize(reply); + string str(_frameBuf.data(), _frameBuf.size()); + _sendChannel->sendStream(xrdsvc::StreamBuffer::createWithMove(str), true); + + LOGS(_log, LOG_LVL_DEBUG, context << " ** SENT **"); +} + +} // namespace lsst::qserv::wpublish diff --git a/src/wpublish/GetDbStatusCommand.h b/src/wpublish/GetDbStatusCommand.h new file mode 100644 index 0000000000..242fd1b49a --- /dev/null +++ b/src/wpublish/GetDbStatusCommand.h @@ -0,0 +1,62 @@ +// -*- LSST-C++ -*- +/* + * LSST Data Management System + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ +#ifndef LSST_QSERV_WPUBLISH_GET_DB_STATUS_COMMAND_H +#define LSST_QSERV_WPUBLISH_GET_DB_STATUS_COMMAND_H + +// System headers +#include + +// Qserv headers +#include "wbase/WorkerCommand.h" + +// Forward declarations +namespace lsst::qserv::wbase { +class SendChannel; +} // namespace lsst::qserv::wbase + +// This header declarations +namespace lsst::qserv::wpublish { + +/** + * Class GetDbStatusCommand returns various info on the status of the database + * service of the Qserv worker. + */ +class GetDbStatusCommand : public wbase::WorkerCommand { +public: + /** + * @param sendChannel The communication channel for reporting results. + */ + GetDbStatusCommand(std::shared_ptr const& sendChannel); + + GetDbStatusCommand() = delete; + GetDbStatusCommand& operator=(GetDbStatusCommand const&) = delete; + GetDbStatusCommand(GetDbStatusCommand const&) = delete; + + virtual ~GetDbStatusCommand() override = default; + +protected: + virtual void run() override; +}; + +} // namespace lsst::qserv::wpublish + +#endif // LSST_QSERV_WPUBLISH_GET_DB_STATUS_COMMAND_H diff --git a/src/xrdreq/CMakeLists.txt b/src/xrdreq/CMakeLists.txt index 6aa8cf95dc..e63e1e58bb 100644 --- a/src/xrdreq/CMakeLists.txt +++ b/src/xrdreq/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(xrdreq PRIVATE ChunkGroupQservRequest.cc ChunkListQservRequest.cc GetChunkListQservRequest.cc + GetDbStatusQservRequest.cc GetStatusQservRequest.cc QservRequest.cc QueryManagementAction.cc diff --git a/src/xrdreq/GetDbStatusQservRequest.cc b/src/xrdreq/GetDbStatusQservRequest.cc new file mode 100644 index 0000000000..f781dc6cd9 --- /dev/null +++ b/src/xrdreq/GetDbStatusQservRequest.cc @@ -0,0 +1,93 @@ +/* + * LSST Data Management System + * Copyright 2018 LSST Corporation. + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ + +// Class header +#include "xrdreq/GetDbStatusQservRequest.h" + +// LSST headers +#include "lsst/log/Log.h" + +using namespace std; + +namespace { + +LOG_LOGGER _log = LOG_GET("lsst.qserv.xrdreq.GetDbStatusQservRequest"); + +} // namespace + +namespace lsst::qserv::xrdreq { + +GetDbStatusQservRequest::Ptr GetDbStatusQservRequest::create(GetDbStatusQservRequest::CallbackType onFinish) { + GetDbStatusQservRequest::Ptr ptr(new GetDbStatusQservRequest(onFinish)); + ptr->setRefToSelf4keepAlive(ptr); + return ptr; +} + +GetDbStatusQservRequest::GetDbStatusQservRequest(GetDbStatusQservRequest::CallbackType onFinish) + : _onFinish(onFinish) { + LOGS(_log, LOG_LVL_DEBUG, "GetDbStatusQservRequest ** CONSTRUCTED **"); +} + +GetDbStatusQservRequest::~GetDbStatusQservRequest() { + LOGS(_log, LOG_LVL_DEBUG, "GetDbStatusQservRequest ** DELETED **"); +} + +void GetDbStatusQservRequest::onRequest(proto::FrameBuffer& buf) { + proto::WorkerCommandH header; + header.set_command(proto::WorkerCommandH::GET_DATABASE_STATUS); + buf.serialize(header); +} + +void GetDbStatusQservRequest::onResponse(proto::FrameBufferView& view) { + proto::WorkerCommandGetDbStatusR reply; + view.parse(reply); + + if (nullptr != _onFinish) { + // Clearing the stored callback after finishing the up-stream notification + // has two purposes: + // + // 1. it guaranties (exactly) one time notification + // 2. it breaks the up-stream dependency on a caller object if a shared + // pointer to the object was mentioned as the lambda-function's closure + + auto onFinish = move(_onFinish); + _onFinish = nullptr; + onFinish(proto::WorkerCommandStatus::SUCCESS, string(), reply.info()); + } +} + +void GetDbStatusQservRequest::onError(string const& error) { + if (nullptr != _onFinish) { + // Clearing the stored callback after finishing the up-stream notification + // has two purposes: + // + // 1. it guaranties (exactly) one time notification + // 2. it breaks the up-stream dependency on a caller object if a shared + // pointer to the object was mentioned as the lambda-function's closure + + auto onFinish = move(_onFinish); + _onFinish = nullptr; + onFinish(proto::WorkerCommandStatus::ERROR, error, string()); + } +} + +} // namespace lsst::qserv::xrdreq diff --git a/src/xrdreq/GetDbStatusQservRequest.h b/src/xrdreq/GetDbStatusQservRequest.h new file mode 100644 index 0000000000..be75c53a6c --- /dev/null +++ b/src/xrdreq/GetDbStatusQservRequest.h @@ -0,0 +1,87 @@ +// -*- LSST-C++ -*- +/* + * LSST Data Management System + * + * This product includes software developed by the + * LSST Project (http://www.lsst.org/). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the LSST License Statement and + * the GNU General Public License along with this program. If not, + * see . + */ +#ifndef LSST_QSERV_XRDREQ_GET_DB_STATUS_QSERV_REQUEST_H +#define LSST_QSERV_XRDREQ_GET_DB_STATUS_QSERV_REQUEST_H + +// System headers +#include +#include +#include +#include +#include + +// Qserv headers +#include "proto/worker.pb.h" +#include "xrdreq/QservRequest.h" + +namespace lsst::qserv::xrdreq { + +/** + * Class GetDbStatusQservRequest represents a request returning various info + * on the status of the database service of the Qserv worker. + */ +class GetDbStatusQservRequest : public QservRequest { +public: + /// The pointer type for instances of the class + typedef std::shared_ptr Ptr; + + /// The callback function type to be used for notifications on + /// the operation completion. + using CallbackType = std::function; // worker info received (if success) + + /** + * Static factory method is needed to prevent issues with the lifespan + * and memory management of instances created otherwise (as values or via + * low-level pointers). + * + * @param onFinish (optional )callback function to be called upon the completion + * (successful or not) of the request. + * @see wbase::Task::Status + * @return the smart pointer to the object of the class + */ + static Ptr create(CallbackType onFinish = nullptr); + + GetDbStatusQservRequest() = delete; + GetDbStatusQservRequest(GetDbStatusQservRequest const&) = delete; + GetDbStatusQservRequest& operator=(GetDbStatusQservRequest const&) = delete; + + virtual ~GetDbStatusQservRequest() override; + +protected: + /// @see GetDbStatusQservRequest::create() + GetDbStatusQservRequest(CallbackType onFinish); + + virtual void onRequest(proto::FrameBuffer& buf) override; + virtual void onResponse(proto::FrameBufferView& view) override; + virtual void onError(std::string const& error) override; + +private: + // Parameters of the object + + CallbackType _onFinish; +}; + +} // namespace lsst::qserv::xrdreq + +#endif // LSST_QSERV_XRDREQ_GET_DB_STATUS_QSERV_REQUEST_H diff --git a/src/xrdsvc/SsiRequest.cc b/src/xrdsvc/SsiRequest.cc index 7dec4df117..a3359b8a36 100644 --- a/src/xrdsvc/SsiRequest.cc +++ b/src/xrdsvc/SsiRequest.cc @@ -53,6 +53,7 @@ #include "wpublish/AddChunkGroupCommand.h" #include "wpublish/ChunkListCommand.h" #include "wpublish/GetChunkListCommand.h" +#include "wpublish/GetDbStatusCommand.h" #include "wpublish/GetStatusCommand.h" #include "wpublish/RemoveChunkGroupCommand.h" #include "wpublish/ResourceMonitor.h" @@ -392,6 +393,10 @@ wbase::WorkerCommand::Ptr SsiRequest::parseWorkerCommand( sendChannel, _foreman, _resourceMonitor, ::proto2taskSelector(message)); break; } + case proto::WorkerCommandH::GET_DATABASE_STATUS: { + command = std::make_shared(sendChannel); + break; + } default: reportError("Unsupported command " + proto::WorkerCommandH_Command_Name(header.command()) + " found in WorkerCommandH on worker resource=" + _resourceName); From 73f72f31b82838c177bb3719a615904979a8b8bb Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Tue, 1 Aug 2023 21:38:29 +0000 Subject: [PATCH 04/12] Reduced logging level in the worker request classes --- src/xrdreq/ChunkGroupQservRequest.cc | 6 +++--- src/xrdreq/ChunkListQservRequest.cc | 10 +++++----- src/xrdreq/GetChunkListQservRequest.cc | 8 ++++---- src/xrdreq/GetDbStatusQservRequest.cc | 4 ++-- src/xrdreq/GetStatusQservRequest.cc | 4 ++-- src/xrdreq/QservRequest.cc | 12 ++++++------ src/xrdreq/QueryManagementAction.cc | 4 ++-- src/xrdreq/QueryManagementRequest.cc | 4 ++-- src/xrdreq/SetChunkListQservRequest.cc | 8 ++++---- src/xrdreq/TestEchoQservRequest.cc | 6 +++--- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/xrdreq/ChunkGroupQservRequest.cc b/src/xrdreq/ChunkGroupQservRequest.cc index e72ff5cceb..0ded683e6b 100644 --- a/src/xrdreq/ChunkGroupQservRequest.cc +++ b/src/xrdreq/ChunkGroupQservRequest.cc @@ -40,12 +40,12 @@ namespace lsst::qserv::xrdreq { ChunkGroupQservRequest::ChunkGroupQservRequest(bool add, unsigned int chunk, vector const& databases, bool force, CallbackType onFinish) : _add(add), _chunk(chunk), _databases(databases), _force(force), _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, + LOGS(_log, LOG_LVL_TRACE, "ChunkGroupQservRequest[" << (_add ? "add" : "remove") << "] ** CONSTRUCTED **"); } ChunkGroupQservRequest::~ChunkGroupQservRequest() { - LOGS(_log, LOG_LVL_DEBUG, "ChunkGroupQservRequest[" << (_add ? "add" : "remove") << "] ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "ChunkGroupQservRequest[" << (_add ? "add" : "remove") << "] ** DELETED **"); } void ChunkGroupQservRequest::onRequest(proto::FrameBuffer& buf) { @@ -67,7 +67,7 @@ void ChunkGroupQservRequest::onResponse(proto::FrameBufferView& view) { proto::WorkerCommandChunkGroupR reply; view.parse(reply); - LOGS(_log, LOG_LVL_DEBUG, + LOGS(_log, LOG_LVL_TRACE, "ChunkGroupQservRequest[" << (_add ? "add" : "remove") << "** SERVICE REPLY ** status: " << proto::WorkerCommandStatus_Code_Name(reply.status().code())); diff --git a/src/xrdreq/ChunkListQservRequest.cc b/src/xrdreq/ChunkListQservRequest.cc index 7d4c641284..839b9d25b0 100644 --- a/src/xrdreq/ChunkListQservRequest.cc +++ b/src/xrdreq/ChunkListQservRequest.cc @@ -39,11 +39,11 @@ namespace lsst::qserv::xrdreq { ChunkListQservRequest::ChunkListQservRequest(bool rebuild, bool reload, CallbackType onFinish) : _rebuild(rebuild), _reload(reload), _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, "ChunkListQservRequest ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "ChunkListQservRequest ** CONSTRUCTED **"); } ChunkListQservRequest::~ChunkListQservRequest() { - LOGS(_log, LOG_LVL_DEBUG, "ChunkListQservRequest ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "ChunkListQservRequest ** DELETED **"); } void ChunkListQservRequest::onRequest(proto::FrameBuffer& buf) { @@ -63,7 +63,7 @@ void ChunkListQservRequest::onResponse(proto::FrameBufferView& view) { proto::WorkerCommandUpdateChunkListR reply; view.parse(reply); - LOGS(_log, LOG_LVL_DEBUG, + LOGS(_log, LOG_LVL_TRACE, context << "** SERVICE REPLY ** status: " << proto::WorkerCommandStatus_Code_Name(reply.status().code())); @@ -77,7 +77,7 @@ void ChunkListQservRequest::onResponse(proto::FrameBufferView& view) { Chunk chunk{chunkEntry.chunk(), chunkEntry.db()}; added.push_back(chunk); } - LOGS(_log, LOG_LVL_DEBUG, context << "total chunks added: " << numAdded); + LOGS(_log, LOG_LVL_TRACE, context << "total chunks added: " << numAdded); int const numRemoved = reply.removed_size(); for (int i = 0; i < numRemoved; i++) { @@ -85,7 +85,7 @@ void ChunkListQservRequest::onResponse(proto::FrameBufferView& view) { Chunk chunk{chunkEntry.chunk(), chunkEntry.db()}; removed.push_back(chunk); } - LOGS(_log, LOG_LVL_DEBUG, context << "total chunks removed: " << numRemoved); + LOGS(_log, LOG_LVL_TRACE, context << "total chunks removed: " << numRemoved); } if (nullptr != _onFinish) { // Clearing the stored callback after finishing the up-stream notification diff --git a/src/xrdreq/GetChunkListQservRequest.cc b/src/xrdreq/GetChunkListQservRequest.cc index 686151b980..2d152e03b6 100644 --- a/src/xrdreq/GetChunkListQservRequest.cc +++ b/src/xrdreq/GetChunkListQservRequest.cc @@ -47,11 +47,11 @@ GetChunkListQservRequest::Ptr GetChunkListQservRequest::create( GetChunkListQservRequest::GetChunkListQservRequest(bool inUseOnly, GetChunkListQservRequest::CallbackType onFinish) : _inUseOnly(inUseOnly), _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, "GetChunkListQservRequest ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "GetChunkListQservRequest ** CONSTRUCTED **"); } GetChunkListQservRequest::~GetChunkListQservRequest() { - LOGS(_log, LOG_LVL_DEBUG, "GetChunkListQservRequest ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "GetChunkListQservRequest ** DELETED **"); } void GetChunkListQservRequest::onRequest(proto::FrameBuffer& buf) { @@ -66,7 +66,7 @@ void GetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { proto::WorkerCommandGetChunkListR reply; view.parse(reply); - LOGS(_log, LOG_LVL_DEBUG, + LOGS(_log, LOG_LVL_TRACE, context << "** SERVICE REPLY ** status: " << proto::WorkerCommandStatus_Code_Name(reply.status().code())); @@ -80,7 +80,7 @@ void GetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { Chunk chunk{chunkEntry.chunk(), chunkEntry.db(), chunkEntry.use_count()}; chunks.push_back(chunk); } - LOGS(_log, LOG_LVL_DEBUG, context << "total chunks: " << num); + LOGS(_log, LOG_LVL_TRACE, context << "total chunks: " << num); } if (nullptr != _onFinish) { // Clearing the stored callback after finishing the up-stream notification diff --git a/src/xrdreq/GetDbStatusQservRequest.cc b/src/xrdreq/GetDbStatusQservRequest.cc index f781dc6cd9..24e1545941 100644 --- a/src/xrdreq/GetDbStatusQservRequest.cc +++ b/src/xrdreq/GetDbStatusQservRequest.cc @@ -44,11 +44,11 @@ GetDbStatusQservRequest::Ptr GetDbStatusQservRequest::create(GetDbStatusQservReq GetDbStatusQservRequest::GetDbStatusQservRequest(GetDbStatusQservRequest::CallbackType onFinish) : _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, "GetDbStatusQservRequest ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "GetDbStatusQservRequest ** CONSTRUCTED **"); } GetDbStatusQservRequest::~GetDbStatusQservRequest() { - LOGS(_log, LOG_LVL_DEBUG, "GetDbStatusQservRequest ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "GetDbStatusQservRequest ** DELETED **"); } void GetDbStatusQservRequest::onRequest(proto::FrameBuffer& buf) { diff --git a/src/xrdreq/GetStatusQservRequest.cc b/src/xrdreq/GetStatusQservRequest.cc index 84f36baa94..930a7beb42 100644 --- a/src/xrdreq/GetStatusQservRequest.cc +++ b/src/xrdreq/GetStatusQservRequest.cc @@ -44,11 +44,11 @@ GetStatusQservRequest::Ptr GetStatusQservRequest::create(wbase::TaskSelector con GetStatusQservRequest::GetStatusQservRequest(wbase::TaskSelector const& taskSelector, GetStatusQservRequest::CallbackType onFinish) : _taskSelector(taskSelector), _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, "GetStatusQservRequest ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "GetStatusQservRequest ** CONSTRUCTED **"); } GetStatusQservRequest::~GetStatusQservRequest() { - LOGS(_log, LOG_LVL_DEBUG, "GetStatusQservRequest ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "GetStatusQservRequest ** DELETED **"); } void GetStatusQservRequest::onRequest(proto::FrameBuffer& buf) { diff --git a/src/xrdreq/QservRequest.cc b/src/xrdreq/QservRequest.cc index 6b038dc696..6310d1c096 100644 --- a/src/xrdreq/QservRequest.cc +++ b/src/xrdreq/QservRequest.cc @@ -49,7 +49,7 @@ QservRequest::~QservRequest() { delete[] _buf; --_numClassInstances; - LOGS(_log, LOG_LVL_DEBUG, "QservRequest destructed instances: " << _numClassInstances); + LOGS(_log, LOG_LVL_TRACE, "QservRequest destructed instances: " << _numClassInstances); } QservRequest::QservRequest() @@ -60,7 +60,7 @@ QservRequest::QservRequest() // This report is used solely for debugging purposes to allow tracking // potential memory leaks within applications. ++_numClassInstances; - LOGS(_log, LOG_LVL_DEBUG, "QservRequest constructed instances: " << _numClassInstances); + LOGS(_log, LOG_LVL_TRACE, "QservRequest constructed instances: " << _numClassInstances); } void QservRequest::cancel() { @@ -113,7 +113,7 @@ bool QservRequest::ProcessResponse(const XrdSsiErrInfo& eInfo, const XrdSsiRespI onError(errorStr); return false; } - LOGS(_log, LOG_LVL_DEBUG, + LOGS(_log, LOG_LVL_TRACE, context << " eInfo.rType: " << rInfo.rType << "(" << rInfo.State() << ")" << ", eInfo.blen: " << rInfo.blen); @@ -121,7 +121,7 @@ bool QservRequest::ProcessResponse(const XrdSsiErrInfo& eInfo, const XrdSsiRespI case XrdSsiRespInfo::isData: case XrdSsiRespInfo::isStream: - LOGS(_log, LOG_LVL_DEBUG, context << "** REQUESTING RESPONSE DATA **"); + LOGS(_log, LOG_LVL_TRACE, context << "** REQUESTING RESPONSE DATA **"); GetResponseData(_buf + _bufSize, _bufIncrementSize); return true; @@ -149,7 +149,7 @@ bool QservRequest::ProcessResponse(const XrdSsiErrInfo& eInfo, const XrdSsiRespI void QservRequest::ProcessResponseData(const XrdSsiErrInfo& eInfo, char* buff, int blen, bool last) { string const context = "QservRequest::" + string(__func__) + " "; - LOGS(_log, LOG_LVL_DEBUG, context << "eInfo.isOK: " << eInfo.isOK()); + LOGS(_log, LOG_LVL_TRACE, context << "eInfo.isOK: " << eInfo.isOK()); if (not eInfo.isOK()) { // This will decrement the reference counter to the pointee at the end of the current @@ -175,7 +175,7 @@ void QservRequest::ProcessResponseData(const XrdSsiErrInfo& eInfo, char* buff, i onError(errorStr); } else { - LOGS(_log, LOG_LVL_DEBUG, context << "blen: " << blen << ", last: " << last); + LOGS(_log, LOG_LVL_TRACE, context << "blen: " << blen << ", last: " << last); // Update the byte counter _bufSize += blen; diff --git a/src/xrdreq/QueryManagementAction.cc b/src/xrdreq/QueryManagementAction.cc index 9882c74da5..18dcb11df1 100644 --- a/src/xrdreq/QueryManagementAction.cc +++ b/src/xrdreq/QueryManagementAction.cc @@ -70,11 +70,11 @@ void QueryManagementAction::notifyAllWorkers(string const& xrootdFrontendUrl, } QueryManagementAction::QueryManagementAction() { - LOGS(_log, LOG_LVL_DEBUG, "QueryManagementAction ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "QueryManagementAction ** CONSTRUCTED **"); } QueryManagementAction::~QueryManagementAction() { - LOGS(_log, LOG_LVL_DEBUG, "QueryManagementAction ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "QueryManagementAction ** DELETED **"); } void QueryManagementAction::_notifyAllWorkers(std::string const& xrootdFrontendUrl, diff --git a/src/xrdreq/QueryManagementRequest.cc b/src/xrdreq/QueryManagementRequest.cc index 0122c7cb99..ea2c001ce4 100644 --- a/src/xrdreq/QueryManagementRequest.cc +++ b/src/xrdreq/QueryManagementRequest.cc @@ -45,11 +45,11 @@ QueryManagementRequest::Ptr QueryManagementRequest::create(proto::QueryManagemen QueryManagementRequest::QueryManagementRequest(proto::QueryManagement::Operation op, QueryId queryId, QueryManagementRequest::CallbackType onFinish) : _op(op), _queryId(queryId), _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, "QueryManagementRequest ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "QueryManagementRequest ** CONSTRUCTED **"); } QueryManagementRequest::~QueryManagementRequest() { - LOGS(_log, LOG_LVL_DEBUG, "QueryManagementRequest ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "QueryManagementRequest ** DELETED **"); } void QueryManagementRequest::onRequest(proto::FrameBuffer& buf) { diff --git a/src/xrdreq/SetChunkListQservRequest.cc b/src/xrdreq/SetChunkListQservRequest.cc index 23ab3226e9..bb3e701a99 100644 --- a/src/xrdreq/SetChunkListQservRequest.cc +++ b/src/xrdreq/SetChunkListQservRequest.cc @@ -49,11 +49,11 @@ SetChunkListQservRequest::SetChunkListQservRequest(SetChunkListQservRequest::Chu vector const& databases, bool force, SetChunkListQservRequest::CallbackType onFinish) : _chunks(chunks), _databases(databases), _force(force), _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, "SetChunkListQservRequest ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "SetChunkListQservRequest ** CONSTRUCTED **"); } SetChunkListQservRequest::~SetChunkListQservRequest() { - LOGS(_log, LOG_LVL_DEBUG, "SetChunkListQservRequest ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "SetChunkListQservRequest ** DELETED **"); } void SetChunkListQservRequest::onRequest(proto::FrameBuffer& buf) { @@ -80,7 +80,7 @@ void SetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { proto::WorkerCommandSetChunkListR reply; view.parse(reply); - LOGS(_log, LOG_LVL_DEBUG, + LOGS(_log, LOG_LVL_TRACE, context << "** SERVICE REPLY ** status: " << proto::WorkerCommandStatus_Code_Name(reply.status().code())); @@ -93,7 +93,7 @@ void SetChunkListQservRequest::onResponse(proto::FrameBufferView& view) { Chunk chunk{chunkEntry.chunk(), chunkEntry.db(), chunkEntry.use_count()}; chunks.push_back(chunk); } - LOGS(_log, LOG_LVL_DEBUG, context << "total chunks: " << num); + LOGS(_log, LOG_LVL_TRACE, context << "total chunks: " << num); } if (nullptr != _onFinish) { // Clearing the stored callback after finishing the up-stream notification diff --git a/src/xrdreq/TestEchoQservRequest.cc b/src/xrdreq/TestEchoQservRequest.cc index ed17a5dd04..2864c27413 100644 --- a/src/xrdreq/TestEchoQservRequest.cc +++ b/src/xrdreq/TestEchoQservRequest.cc @@ -46,11 +46,11 @@ TestEchoQservRequest::Ptr TestEchoQservRequest::create(string const& value, TestEchoQservRequest::TestEchoQservRequest(string const& value, TestEchoQservRequest::CallbackType onFinish) : _value(value), _onFinish(onFinish) { - LOGS(_log, LOG_LVL_DEBUG, "TestEchoQservRequest ** CONSTRUCTED **"); + LOGS(_log, LOG_LVL_TRACE, "TestEchoQservRequest ** CONSTRUCTED **"); } TestEchoQservRequest::~TestEchoQservRequest() { - LOGS(_log, LOG_LVL_DEBUG, "TestEchoQservRequest ** DELETED **"); + LOGS(_log, LOG_LVL_TRACE, "TestEchoQservRequest ** DELETED **"); } void TestEchoQservRequest::onRequest(proto::FrameBuffer& buf) { @@ -67,7 +67,7 @@ void TestEchoQservRequest::onResponse(proto::FrameBufferView& view) { proto::WorkerCommandTestEchoR reply; view.parse(reply); - LOGS(_log, LOG_LVL_DEBUG, + LOGS(_log, LOG_LVL_TRACE, "TestEchoQservRequest ** SERVICE REPLY ** status: " << proto::WorkerCommandStatus_Code_Name(reply.status().code())); From 62a32bed8f9f9a4eec04380bfc4c0c1fdb62706b Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Mon, 7 Aug 2023 17:36:02 +0000 Subject: [PATCH 05/12] Minor refactoring in the Controller REST services --- src/replica/HttpProcessor.cc | 9 +++------ src/replica/HttpQservMonitorModule.cc | 11 ++++++----- src/replica/HttpQservMonitorModule.h | 14 +++++++------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/replica/HttpProcessor.cc b/src/replica/HttpProcessor.cc index e00f385f12..7ab0dff2ba 100644 --- a/src/replica/HttpProcessor.cc +++ b/src/replica/HttpProcessor.cc @@ -236,8 +236,7 @@ void HttpProcessor::registerServices() { httpServer()->addHandler("GET", "/replication/qserv/worker/status/:worker", [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { HttpQservMonitorModule::process(self->controller(), self->name(), - self->_processorConfig, req, resp, - "SELECT-WORKER-BY-NAME"); + self->_processorConfig, req, resp, "WORKER"); }); httpServer()->addHandler("GET", "/replication/qserv/master/query", [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { @@ -248,14 +247,12 @@ void HttpProcessor::registerServices() { httpServer()->addHandler("GET", "/replication/qserv/master/query/:id", [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { HttpQservMonitorModule::process(self->controller(), self->name(), - self->_processorConfig, req, resp, - "SELECT-QUERY-BY-ID"); + self->_processorConfig, req, resp, "QUERY"); }); httpServer()->addHandler("GET", "/replication/qserv/css/shared-scan", [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { HttpQservMonitorModule::process(self->controller(), self->name(), - self->_processorConfig, req, resp, - "CSS-SHARED-SCAN"); + self->_processorConfig, req, resp, "CSS"); }); httpServer()->addHandler("GET", "/replication/sql/table/schema/:database/:table", [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { diff --git a/src/replica/HttpQservMonitorModule.cc b/src/replica/HttpQservMonitorModule.cc index 447da0f360..3f8c10074f 100644 --- a/src/replica/HttpQservMonitorModule.cc +++ b/src/replica/HttpQservMonitorModule.cc @@ -38,6 +38,7 @@ #include "replica/Configuration.h" #include "replica/ConfigDatabase.h" #include "replica/DatabaseServices.h" +#include "replica/HttpExceptions.h" #include "replica/QservMgtServices.h" #include "replica/QservStatusJob.h" #include "replica/ServiceProvider.h" @@ -136,14 +137,14 @@ HttpQservMonitorModule::HttpQservMonitorModule(Controller::Ptr const& controller json HttpQservMonitorModule::executeImpl(string const& subModuleName) { if (subModuleName == "WORKERS") return _workers(); - else if (subModuleName == "SELECT-WORKER-BY-NAME") + else if (subModuleName == "WORKER") return _worker(); else if (subModuleName == "QUERIES") return _userQueries(); - else if (subModuleName == "SELECT-QUERY-BY-ID") + else if (subModuleName == "QUERY") return _userQuery(); - else if (subModuleName == "CSS-SHARED-SCAN") - return _cssSharedScan(); + else if (subModuleName == "CSS") + return _css(); throw invalid_argument(context() + "::" + string(__func__) + " unsupported sub-module: '" + subModuleName + "'"); } @@ -535,7 +536,7 @@ json HttpQservMonitorModule::_getQueries(json const& workerInfo) const { return result; } -json HttpQservMonitorModule::_cssSharedScan() { +json HttpQservMonitorModule::_css() { debug(__func__); checkApiVersion(__func__, 12); diff --git a/src/replica/HttpQservMonitorModule.h b/src/replica/HttpQservMonitorModule.h index d2565061e3..76cd396880 100644 --- a/src/replica/HttpQservMonitorModule.h +++ b/src/replica/HttpQservMonitorModule.h @@ -54,11 +54,11 @@ class HttpQservMonitorModule : public HttpModule { /** * Supported values for parameter 'subModuleName': * - * WORKERS for many workers (possible selected by various criteria) - * SELECT-WORKER-BY-NAME for a specific worker - * QUERIES for many queries (selected by various criteria) - * SELECT-QUERY-BY-ID for a specific query - * CSS-SHARED-SCAN get CSS configurations of the shared scan for all tables + * WORKERS get the status info of many workers (possible selected by various criteria) + * WORKER get the status info of a specific worker + * QUERIES get user query info (queries selected by various criteria) + * QUERY get user query info for a specific query + * CSS get CSS configurations (the shared scan settings, etc.) * * @throws std::invalid_argument for unknown values of parameter 'subModuleName' */ @@ -161,8 +161,8 @@ class HttpQservMonitorModule : public HttpModule { */ nlohmann::json _getQueries(nlohmann::json const& workerInfo) const; - /// @return The shared scan parameters of all partitioned tables (CSS) - nlohmann::json _cssSharedScan(); + /// @return The CSS info (shared scan parameters of all partitioned tables, etc.) + nlohmann::json _css(); /** * @param chunks collection of chunks numbers to be expanded From 99c7e20a8c8591d9f147c2fd1edfa4a81e83d626 Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Fri, 11 Aug 2023 16:17:16 +0000 Subject: [PATCH 06/12] Extended Replication system's database API Added a function for getting info on the ongoing database activities as reported by the query SHOW [FULL] PROCESSLIST. --- src/replica/DatabaseMySQLUtils.cc | 30 ++++++++++++++++++++++++++++-- src/replica/DatabaseMySQLUtils.h | 17 +++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/replica/DatabaseMySQLUtils.cc b/src/replica/DatabaseMySQLUtils.cc index 1e8215c55e..4317ae9bd6 100644 --- a/src/replica/DatabaseMySQLUtils.cc +++ b/src/replica/DatabaseMySQLUtils.cc @@ -31,8 +31,10 @@ #include using namespace std; +using json = nlohmann::json; -namespace lsst::qserv::replica::database::mysql::detail { +namespace lsst::qserv::replica::database::mysql { +namespace detail { bool selectSingleValueImpl(shared_ptr const& conn, string const& query, function const& onEachRow, bool noMoreThanOne) { @@ -62,4 +64,28 @@ bool selectSingleValueImpl(shared_ptr const& conn, string const& que throw logic_error(context + "result set has more than 1 row"); } -} // namespace lsst::qserv::replica::database::mysql::detail +} // namespace detail + +json processList(shared_ptr const& conn, bool full) { + string const query = "SHOW" + string(full ? " FULL" : "") + " PROCESSLIST"; + json result; + result["queries"] = json::object({{"columns", json::array()}, {"rows", json::array()}}); + conn->executeInOwnTransaction([&](auto conn) { + conn->execute(query); + if (conn->hasResult()) { + result["queries"]["columns"] = conn->columnNames(); + auto& rows = result["queries"]["rows"]; + Row row; + while (conn->next(row)) { + json resultRow = json::array(); + for (size_t colIdx = 0, numColumns = row.numColumns(); colIdx < numColumns; ++colIdx) { + resultRow.push_back(row.getAs(colIdx, string())); + } + rows.push_back(resultRow); + } + } + }); + return result; +} + +} // namespace lsst::qserv::replica::database::mysql diff --git a/src/replica/DatabaseMySQLUtils.h b/src/replica/DatabaseMySQLUtils.h index 6d9c198678..4ca34a243b 100644 --- a/src/replica/DatabaseMySQLUtils.h +++ b/src/replica/DatabaseMySQLUtils.h @@ -26,6 +26,9 @@ #include #include +// Third-party headers +#include "nlohmann/json.hpp" + // Qserv headers #include "replica/Common.h" #include "replica/DatabaseMySQLRow.h" @@ -81,6 +84,20 @@ inline bool selectSingleValue(std::shared_ptr const& conn, std::stri return detail::selectSingleValueImpl(conn, query, onEachRow, noMoreThanOne); } +/** + * Report info on the on-going queries using 'SHOW [FULL] PROCESSLIST'. + * @param A scope of the operaton depends on the user credentials privided + * in the configuration object. Normally, a subset of queries which belong + * to the specified user will be reported. + * @param conn The MySQL connection for executing the query. + * @param full The optional modifier which (if set) allows seeing the full text + * of the queries. + * @return A collection of queries encoded as the JSON object. Please, see the code + * for further details on the schema of the object. + * @throws mysql::Error on errors detected during query execution/processing. + */ +nlohmann::json processList(std::shared_ptr const& conn, bool full = false); + } // namespace lsst::qserv::replica::database::mysql #endif // LSST_QSERV_REPLICA_DATABASEMYSQLUTILS_H From 951df6e55068c2560b0155d6ecfd2d729f8cf3cf Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Tue, 1 Aug 2023 22:29:44 +0000 Subject: [PATCH 07/12] Extended Controller's REST API to report database activities Added the REST services to report database activities at Czar and Qserv workers. Incremented the version number of the REST API. --- .../lsst/qserv/admin/replicationInterface.py | 2 +- src/replica/HttpMetaModule.cc | 2 +- src/replica/HttpProcessor.cc | 12 +++++ src/replica/HttpQservMonitorModule.cc | 45 +++++++++++++++++++ src/replica/HttpQservMonitorModule.h | 14 ++++++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/admin/python/lsst/qserv/admin/replicationInterface.py b/src/admin/python/lsst/qserv/admin/replicationInterface.py index 9cdaafd880..4d15f3146f 100644 --- a/src/admin/python/lsst/qserv/admin/replicationInterface.py +++ b/src/admin/python/lsst/qserv/admin/replicationInterface.py @@ -201,7 +201,7 @@ def __init__( self.repl_ctrl = urlparse(repl_ctrl_uri) self.auth_key = auth_key self.admin_auth_key = admin_auth_key - self.repl_api_version = 23 + self.repl_api_version = 24 _log.debug(f"ReplicationInterface %s", self.repl_ctrl) def version(self) -> str: diff --git a/src/replica/HttpMetaModule.cc b/src/replica/HttpMetaModule.cc index 85cb9a1e5a..f1a476d2b8 100644 --- a/src/replica/HttpMetaModule.cc +++ b/src/replica/HttpMetaModule.cc @@ -34,7 +34,7 @@ using json = nlohmann::json; namespace lsst::qserv::replica { -unsigned int const HttpMetaModule::version = 23; +unsigned int const HttpMetaModule::version = 24; void HttpMetaModule::process(ServiceProvider::Ptr const& serviceProvider, string const& context, qhttp::Request::Ptr const& req, qhttp::Response::Ptr const& resp, diff --git a/src/replica/HttpProcessor.cc b/src/replica/HttpProcessor.cc index 7ab0dff2ba..7baa25268f 100644 --- a/src/replica/HttpProcessor.cc +++ b/src/replica/HttpProcessor.cc @@ -238,6 +238,18 @@ void HttpProcessor::registerServices() { HttpQservMonitorModule::process(self->controller(), self->name(), self->_processorConfig, req, resp, "WORKER"); }); + httpServer()->addHandler("GET", "/replication/qserv/worker/db/:worker", + [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { + HttpQservMonitorModule::process(self->controller(), self->name(), + self->_processorConfig, req, resp, + "WORKER-DB"); + }); + httpServer()->addHandler("GET", "/replication/qserv/master/db", + [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { + HttpQservMonitorModule::process(self->controller(), self->name(), + self->_processorConfig, req, resp, + "CZAR-DB"); + }); httpServer()->addHandler("GET", "/replication/qserv/master/query", [self](qhttp::Request::Ptr const req, qhttp::Response::Ptr const resp) { HttpQservMonitorModule::process(self->controller(), self->name(), diff --git a/src/replica/HttpQservMonitorModule.cc b/src/replica/HttpQservMonitorModule.cc index 3f8c10074f..49728b7f65 100644 --- a/src/replica/HttpQservMonitorModule.cc +++ b/src/replica/HttpQservMonitorModule.cc @@ -33,6 +33,9 @@ // Qserv headers #include "css/CssAccess.h" #include "css/CssError.h" +#include "replica/DatabaseMySQL.h" +#include "replica/DatabaseMySQLTypes.h" +#include "replica/DatabaseMySQLUtils.h" #include "global/intTypes.h" #include "replica/Common.h" #include "replica/Configuration.h" @@ -139,6 +142,10 @@ json HttpQservMonitorModule::executeImpl(string const& subModuleName) { return _workers(); else if (subModuleName == "WORKER") return _worker(); + else if (subModuleName == "WORKER-DB") + return _workerDb(); + else if (subModuleName == "CZAR-DB") + return _czarDb(); else if (subModuleName == "QUERIES") return _userQueries(); else if (subModuleName == "QUERY") @@ -217,6 +224,44 @@ json HttpQservMonitorModule::_worker() { return result; } +json HttpQservMonitorModule::_workerDb() { + debug(__func__); + checkApiVersion(__func__, 24); + + auto const worker = params().at("worker"); + unsigned int const timeoutSec = query().optionalUInt("timeout_sec", workerResponseTimeoutSec()); + + debug(__func__, "worker=" + worker); + debug(__func__, "timeout_sec=" + to_string(timeoutSec)); + + string const noParentJobId; + GetDbStatusQservMgtRequest::CallbackType const onFinish = nullptr; + + auto const request = controller()->serviceProvider()->qservMgtServices()->databaseStatus( + worker, noParentJobId, onFinish, timeoutSec); + request->wait(); + + if (request->extendedState() != QservMgtRequest::ExtendedState::SUCCESS) { + string const msg = "database operation failed, error: " + + QservMgtRequest::state2string(request->extendedState()); + throw HttpError(__func__, msg); + } + json result = json::object(); + result["status"] = request->info(); + return result; +} + +json HttpQservMonitorModule::_czarDb() { + debug(__func__); + checkApiVersion(__func__, 24); + + // Connect to the master database. Manage the new connection via the RAII-style + // handler to ensure the transaction is automatically rolled-back in case of exceptions. + ConnectionHandler const h(Connection::open(Configuration::qservCzarDbParams("qservMeta"))); + bool const full = true; + return json::object({{"status", database::mysql::processList(h.conn, full)}}); +} + wbase::TaskSelector HttpQservMonitorModule::_translateTaskSelector(string const& func) const { wbase::TaskSelector selector; selector.includeTasks = query().optionalUInt("include_tasks", 0) != 0; diff --git a/src/replica/HttpQservMonitorModule.h b/src/replica/HttpQservMonitorModule.h index 76cd396880..e3798105bb 100644 --- a/src/replica/HttpQservMonitorModule.h +++ b/src/replica/HttpQservMonitorModule.h @@ -56,6 +56,8 @@ class HttpQservMonitorModule : public HttpModule { * * WORKERS get the status info of many workers (possible selected by various criteria) * WORKER get the status info of a specific worker + * WORKER-DB get the database status of a specific worker + * CZAR-DB get the database status of Czar * QUERIES get user query info (queries selected by various criteria) * QUERY get user query info for a specific query * CSS get CSS configurations (the shared scan settings, etc.) @@ -94,6 +96,18 @@ class HttpQservMonitorModule : public HttpModule { */ nlohmann::json _worker(); + /** + * Process a request for extracting various status info on the database + * service for select Qserv worker. + */ + nlohmann::json _workerDb(); + + /** + * Process a request for extracting various status info on the database + * service of Czar. + */ + nlohmann::json _czarDb(); + /** * Process a request for extracting a status on select user queries * launched at Qserv. From 098cf37c308477696d3acd9dcc09bc01ddab72f0 Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Wed, 9 Aug 2023 16:56:49 +0000 Subject: [PATCH 08/12] Web Dashboard: rearranged pages for intuitive search --- src/www/dashboard.html | 2 +- src/www/qserv/js/QservMonitoringDashboard.js | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/www/dashboard.html b/src/www/dashboard.html index a97b6e9d52..c8b950a6c7 100644 --- a/src/www/dashboard.html +++ b/src/www/dashboard.html @@ -4,7 +4,7 @@ Qserv monitoring dashboard - + diff --git a/src/www/qserv/js/QservMonitoringDashboard.js b/src/www/qserv/js/QservMonitoringDashboard.js index 2facd2a454..3e94a2cdcd 100644 --- a/src/www/qserv/js/QservMonitoringDashboard.js +++ b/src/www/qserv/js/QservMonitoringDashboard.js @@ -143,12 +143,12 @@ function(CSSLoader, { name: 'Replication', apps: [ new ReplicationController('Controller'), - new ReplicationTools('Tools'), new ReplicationConfigGeneral('Config/General'), new ReplicationConfigWorkers('Config/Workers'), new ReplicationConfigCatalogs('Config/Catalogs'), new ReplicationSchema('Schema'), - new ReplicationTableIndexes('Table Indexes') + new ReplicationTableIndexes('Table Indexes'), + new ReplicationTools('Tools') ] }, { name: 'Ingest', @@ -161,13 +161,12 @@ function(CSSLoader, new IngestContribInfo('Contribution Info') ] }, - { name: 'Tools', + { name: 'Czar', apps: [ - new FwkTestApp('Query Qserv'), - new ToolsSql('Query Worker Databases') + new QservCss('CSS') ] }, - { name: 'Qserv Monitor', + { name: 'Workers', apps: [ new QservMySQLConnections('MySQL Connections'), new QservWorkerQueries('Queries in Worker Queues'), @@ -175,8 +174,13 @@ function(CSSLoader, new QservWorkerSchedulerHist('Scheduler Histograms'), new QservWorkerTasks('Tasks'), new QservWorkerTaskHist('Task Histograms'), - new QservWorkerResultsFilesystem('Results Filesystem'), - new QservCss('CSS') + new QservWorkerResultsFilesystem('Results Filesystem') + ] + }, + { name: 'Tools', + apps: [ + new FwkTestApp('Query Qserv'), + new ToolsSql('Query Worker Databases') ] } ]; From 3f550dce3e5c6badb89ebfe3ed1b87ecaaa1f002 Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Wed, 9 Aug 2023 19:17:07 +0000 Subject: [PATCH 09/12] Web Dashboard: added pages for displaying MySQL queries at Czar and workers --- src/www/qserv/css/QservCzarMySQLQueries.css | 27 ++ src/www/qserv/css/QservWorkerMySQLQueries.css | 27 ++ src/www/qserv/js/Common.js | 2 +- src/www/qserv/js/QservCzarMySQLQueries.js | 199 +++++++++++++++ src/www/qserv/js/QservMonitoringDashboard.js | 6 + src/www/qserv/js/QservWorkerMySQLQueries.js | 238 ++++++++++++++++++ 6 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 src/www/qserv/css/QservCzarMySQLQueries.css create mode 100644 src/www/qserv/css/QservWorkerMySQLQueries.css create mode 100644 src/www/qserv/js/QservCzarMySQLQueries.js create mode 100644 src/www/qserv/js/QservWorkerMySQLQueries.js diff --git a/src/www/qserv/css/QservCzarMySQLQueries.css b/src/www/qserv/css/QservCzarMySQLQueries.css new file mode 100644 index 0000000000..fe1f6806d0 --- /dev/null +++ b/src/www/qserv/css/QservCzarMySQLQueries.css @@ -0,0 +1,27 @@ +#fwk-czar-mysql-queries-controls label { + font-weight: bold; +} +table#fwk-czar-mysql-queries caption { + caption-side: top; + text-align: right; + padding-top: 0; +} +table#fwk-czar-mysql-queries > thead > tr > th.sticky { + position:sticky; + top:80px; + z-index:2; +} +table#fwk-czar-mysql-queries tbody th, +table#fwk-czar-mysql-queries tbody td { + vertical-align:middle; +} +table#fwk-czar-mysql-queries pre { + padding: 0; + margin: 0; +} +table#fwk-czar-mysql-queries tbody > tr > td.query_toggler:hover { + cursor:pointer; +} +table#fwk-czar-mysql-queries caption.updating { + background-color: #ffeeba; +} diff --git a/src/www/qserv/css/QservWorkerMySQLQueries.css b/src/www/qserv/css/QservWorkerMySQLQueries.css new file mode 100644 index 0000000000..3e681745bb --- /dev/null +++ b/src/www/qserv/css/QservWorkerMySQLQueries.css @@ -0,0 +1,27 @@ +#fwk-worker-mysql-queries-controls label { + font-weight: bold; +} +table#fwk-worker-mysql-queries caption { + caption-side: top; + text-align: right; + padding-top: 0; +} +table#fwk-worker-mysql-queries > thead > tr > th.sticky { + position:sticky; + top:80px; + z-index:2; +} +table#fwk-worker-mysql-queries tbody th, +table#fwk-worker-mysql-queries tbody td { + vertical-align:middle; +} +table#fwk-worker-mysql-queries pre { + padding: 0; + margin: 0; +} +table#fwk-worker-mysql-queries tbody > tr > td.query_toggler:hover { + cursor:pointer; +} +table#fwk-worker-mysql-queries caption.updating { + background-color: #ffeeba; +} diff --git a/src/www/qserv/js/Common.js b/src/www/qserv/js/Common.js index d74d31fb74..6fd085e40c 100644 --- a/src/www/qserv/js/Common.js +++ b/src/www/qserv/js/Common.js @@ -3,7 +3,7 @@ define([ function(sqlFormatter) { class Common { - static RestAPIVersion = 23; + static RestAPIVersion = 24; static query2text(query, expanded) { if (expanded) { return sqlFormatter.format(query, Common._sqlFormatterConfig); diff --git a/src/www/qserv/js/QservCzarMySQLQueries.js b/src/www/qserv/js/QservCzarMySQLQueries.js new file mode 100644 index 0000000000..b00a7ab166 --- /dev/null +++ b/src/www/qserv/js/QservCzarMySQLQueries.js @@ -0,0 +1,199 @@ +define([ + 'webfwk/CSSLoader', + 'webfwk/Fwk', + 'webfwk/FwkApplication', + 'qserv/Common', + 'underscore'], + +function(CSSLoader, + Fwk, + FwkApplication, + Common, + _) { + + CSSLoader.load('qserv/css/QservCzarMySQLQueries.css'); + + class QservCzarMySQLQueries extends FwkApplication { + + constructor(name) { + super(name); + this._queryId2Expanded = {}; // Store 'true' to allow persistent state for the expanded + // queries between updates. + this._id2query = {}; // Store query text for each identifier. The dictionary gets + // updated at each refresh of the page. + } + fwk_app_on_show() { + console.log('show: ' + this.fwk_app_name); + this.fwk_app_on_update(); + } + fwk_app_on_hide() { + console.log('hide: ' + this.fwk_app_name); + } + fwk_app_on_update() { + if (this.fwk_app_visible) { + this._init(); + if (this._prev_update_sec === undefined) { + this._prev_update_sec = 0; + } + let now_sec = Fwk.now().sec; + if (now_sec - this._prev_update_sec > this._update_interval_sec()) { + this._prev_update_sec = now_sec; + this._init(); + this._load(); + } + } + } + _init() { + if (this._initialized === undefined) this._initialized = false; + if (this._initialized) return; + this._initialized = true; + let html = ` +
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+ + + + + + + + + + + + +
IdTimeStateQuery
Loading...
+
+
`; + let cont = this.fwk_app_container.html(html); + cont.find(".form-control-selector").change(() => { + this._load(); + }); + cont.find("button#reset-controls-form").click(() => { + this._set_update_interval_sec(10); + this._load(); + }); + } + _form_control(elem_type, id) { + if (this._form_control_obj === undefined) this._form_control_obj = {}; + if (!_.has(this._form_control_obj, id)) { + this._form_control_obj[id] = this.fwk_app_container.find(elem_type + '#' + id); + } + return this._form_control_obj[id]; + } + _update_interval_sec() { return this._form_control('select', 'update-interval').val(); } + _set_update_interval_sec(val) { this._form_control('select', 'update-interval').val(val); } + _table() { + if (this._table_obj === undefined) { + this._table_obj = this.fwk_app_container.find('table#fwk-czar-mysql-queries'); + } + return this._table_obj; + } + _load() { + if (this._loading === undefined) this._loading = false; + if (this._loading) return; + this._loading = true; + this._table().children('caption').addClass('updating'); + Fwk.web_service_GET( + "/replication/qserv/master/db", + { timeout_sec: 2, version: Common.RestAPIVersion + }, + (data) => { + if (data.success) { + this._display(data.status.queries); + Fwk.setLastUpdate(this._table().children('caption')); + } else { + console.log('request failed', this.fwk_app_name, data.error); + this._table().children('caption').html('' + data.error + ''); + } + this._table().children('caption').removeClass('updating'); + this._loading = false; + }, + (msg) => { + console.log('request failed', this.fwk_app_name, msg); + this._table().children('caption').html('No Response'); + this._table().children('caption').removeClass('updating'); + this._loading = false; + } + ); + } + _display(queries) { + const queryCopyTitle = "Click to copy the query text to the clipboard."; + const COL_Id = 0, COL_Command = 4, COL_Time = 5, COL_State = 6, COL_Info = 7; + let tbody = this._table().children('tbody'); + if (_.isEmpty(queries.columns)) { + tbody.html(''); + return; + } + this._id2query = {}; + let html = ''; + for (let i in queries.rows) { + let row = queries.rows[i]; + if (row[COL_Command] !== 'Query') continue; + let queryId = row[COL_Id]; + let query = row[COL_Info]; + this._id2query[queryId] = query; + const expanded = (queryId in this._queryId2Expanded) && this._queryId2Expanded[queryId]; + const queryToggleTitle = "Click to toggle query formatting."; + const queryStyle = "color:#4d4dff;"; + html += ` + +
${queryId}
+
${row[COL_Time]}
+
${row[COL_State]}
+ + + +
` + this._query2text(queryId, expanded) + `

+`;
+            }
+            tbody.html(html);
+            let that = this;
+            let copyQueryToClipboard = function(e) {
+                let button = $(e.currentTarget);
+                let queryId = button.parent().parent().attr("id");
+                let query = that._id2query[queryId];
+                navigator.clipboard.writeText(query,
+                    () => {},
+                    () => { alert("Failed to write the query to the clipboard. Please copy the text manually: " + query); }
+                );
+            };
+            let toggleQueryDisplay = function(e) {
+                let td = $(e.currentTarget);
+                let pre = td.find("pre.query");
+                const queryId = td.parent().attr("id");
+                const expanded = !((queryId in that._queryId2Expanded) && that._queryId2Expanded[queryId]);
+                pre.text(that._query2text(queryId, expanded));
+                that._queryId2Expanded[queryId] = expanded;
+            };
+            tbody.find("button.copy-query").click(copyQueryToClipboard);
+            tbody.find("td.query_toggler").click(toggleQueryDisplay);
+        }
+        _query2text(queryId, expanded) {
+            return Common.query2text(this._id2query[queryId], expanded);
+        }
+    }
+    return QservCzarMySQLQueries;
+});
diff --git a/src/www/qserv/js/QservMonitoringDashboard.js b/src/www/qserv/js/QservMonitoringDashboard.js
index 3e94a2cdcd..502e207f59 100644
--- a/src/www/qserv/js/QservMonitoringDashboard.js
+++ b/src/www/qserv/js/QservMonitoringDashboard.js
@@ -41,8 +41,10 @@ require([
     'qserv/StatusReplicationLevel',
     'qserv/StatusWorkers',
     'qserv/StatusUserQueries',
+    'qserv/QservCzarMySQLQueries',
     'qserv/QservCss',
     'qserv/QservMySQLConnections',
+    'qserv/QservWorkerMySQLQueries',
     'qserv/QservWorkerQueries',
     'qserv/QservWorkerSchedulers',
     'qserv/QservWorkerSchedulerHist',
@@ -79,8 +81,10 @@ function(CSSLoader,
          StatusReplicationLevel,
          StatusWorkers,
          StatusUserQueries,
+         QservCzarMySQLQueries,
          QservCss,
          QservMySQLConnections,
+         QservWorkerMySQLQueries,
          QservWorkerQueries,
          QservWorkerSchedulers,
          QservWorkerSchedulerHist,
@@ -163,12 +167,14 @@ function(CSSLoader,
             },
             {   name: 'Czar',
                 apps: [
+                    new QservCzarMySQLQueries('MySQL Queries'),
                     new QservCss('CSS')
                 ]
             },
             {   name: 'Workers',
                 apps: [
                     new QservMySQLConnections('MySQL Connections'),
+                    new QservWorkerMySQLQueries('MySQL Queries'),
                     new QservWorkerQueries('Queries in Worker Queues'),
                     new QservWorkerSchedulers('Schedulers'),
                     new QservWorkerSchedulerHist('Scheduler Histograms'),
diff --git a/src/www/qserv/js/QservWorkerMySQLQueries.js b/src/www/qserv/js/QservWorkerMySQLQueries.js
new file mode 100644
index 0000000000..a5d033351c
--- /dev/null
+++ b/src/www/qserv/js/QservWorkerMySQLQueries.js
@@ -0,0 +1,238 @@
+define([
+    'webfwk/CSSLoader',
+    'webfwk/Fwk',
+    'webfwk/FwkApplication',
+    'qserv/Common',
+    'underscore'],
+
+function(CSSLoader,
+         Fwk,
+         FwkApplication,
+         Common,
+         _) {
+
+    CSSLoader.load('qserv/css/QservWorkerMySQLQueries.css');
+
+    class QservWorkerMySQLQueries extends FwkApplication {
+
+        constructor(name) {
+            super(name);
+            this._queryId2Expanded = {};    // Store 'true' to allow persistent state for the expanded
+                                            // queries between updates.
+            this._id2query = {};            // Store query text for each identifier. The dictionary gets
+                                            // updated at each refresh of the page.
+        }
+        fwk_app_on_show() {
+            console.log('show: ' + this.fwk_app_name);
+            this.fwk_app_on_update();
+        }
+        fwk_app_on_hide() {
+            console.log('hide: ' + this.fwk_app_name);
+        }
+        fwk_app_on_update() {
+            if (this.fwk_app_visible) {
+                this._init();
+                if (this._prev_update_sec === undefined) {
+                    this._prev_update_sec = 0;
+                }
+                let now_sec = Fwk.now().sec;
+                if (now_sec - this._prev_update_sec > this._update_interval_sec()) {
+                    this._prev_update_sec = now_sec;
+                    this._init();
+                    this._load();
+                }
+            }
+        }
+        _init() {
+            if (this._initialized === undefined) this._initialized = false;
+            if (this._initialized) return;
+            this._initialized = true;
+            let html = `
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ + + + + + + + + + + + +
IdTimeStateQuery
Loading...
+
+
`; + let cont = this.fwk_app_container.html(html); + cont.find(".form-control-selector").change(() => { + this._load(); + }); + cont.find("button#reset-controls-form").click(() => { + this._set_update_interval_sec(10); + this._load(); + }); + } + _form_control(elem_type, id) { + if (this._form_control_obj === undefined) this._form_control_obj = {}; + if (!_.has(this._form_control_obj, id)) { + this._form_control_obj[id] = this.fwk_app_container.find(elem_type + '#' + id); + } + return this._form_control_obj[id]; + } + _update_interval_sec() { return this._form_control('select', 'update-interval').val(); } + _set_update_interval_sec(val) { this._form_control('select', 'update-interval').val(val); } + _worker() { return this._form_control('select', 'worker').val(); } + _set_worker(val) { this._form_control('select', 'worker').val(val); } + _set_workers(workers) { + const prev_worker = this._worker(); + let html = ''; + for (let i in workers) { + const worker = workers[i]; + const selected = (_.isEmpty(prev_worker) && (i === 0)) || + (!_.isEmpty(prev_worker) && (prev_worker === worker)); + html += ` + `; + } + this._form_control('select', 'worker').html(html); + } + _table() { + if (this._table_obj === undefined) { + this._table_obj = this.fwk_app_container.find('table#fwk-worker-mysql-queries'); + } + return this._table_obj; + } + _load() { + if (this._loading === undefined) this._loading = false; + if (this._loading) return; + this._loading = true; + this._table().children('caption').addClass('updating'); + Fwk.web_service_GET( + "/replication/config", + {version: Common.RestAPIVersion}, + (data) => { + let workers = []; + for (let i in data.config.workers) { + workers.push(data.config.workers[i].name); + } + this._set_workers(workers); + this._load_queries(); + }, + (msg) => { + console.log('request failed', this.fwk_app_name, msg); + this._table().children('caption').html('No Response'); + this._table().children('caption').removeClass('updating'); + this._loading = false; + } + ); + } + _load_queries() { + Fwk.web_service_GET( + "/replication/qserv/worker/db/" + this._worker(), + { timeout_sec: 2, version: Common.RestAPIVersion + }, + (data) => { + if (data.success) { + this._display(data.status.queries); + Fwk.setLastUpdate(this._table().children('caption')); + } else { + console.log('request failed', this.fwk_app_name, data.error); + this._table().children('caption').html('' + data.error + ''); + } + this._table().children('caption').removeClass('updating'); + this._loading = false; + }, + (msg) => { + console.log('request failed', this.fwk_app_name, msg); + this._table().children('caption').html('No Response'); + this._table().children('caption').removeClass('updating'); + this._loading = false; + } + ); + } + _display(queries) { + const queryCopyTitle = "Click to copy the query text to the clipboard."; + const COL_Id = 0, COL_Command = 4, COL_Time = 5, COL_State = 6, COL_Info = 7; + let tbody = this._table().children('tbody'); + if (_.isEmpty(queries.columns)) { + tbody.html(''); + return; + } + this._id2query = {}; + let html = ''; + for (let i in queries.rows) { + let row = queries.rows[i]; + if (row[COL_Command] !== 'Query') continue; + let queryId = row[COL_Id]; + let query = row[COL_Info]; + this._id2query[queryId] = query; + const expanded = (queryId in this._queryId2Expanded) && this._queryId2Expanded[queryId]; + const queryToggleTitle = "Click to toggle query formatting."; + const queryStyle = "color:#4d4dff;"; + html += ` + +
${queryId}
+
${row[COL_Time]}
+
${row[COL_State]}
+ + + +
` + this._query2text(queryId, expanded) + `

+`;
+            }
+            tbody.html(html);
+            let that = this;
+            let copyQueryToClipboard = function(e) {
+                let button = $(e.currentTarget);
+                let queryId = button.parent().parent().attr("id");
+                let query = that._id2query[queryId];
+                navigator.clipboard.writeText(query,
+                    () => {},
+                    () => { alert("Failed to write the query to the clipboard. Please copy the text manually: " + query); }
+                );
+            };
+            let toggleQueryDisplay = function(e) {
+                let td = $(e.currentTarget);
+                let pre = td.find("pre.query");
+                const queryId = td.parent().attr("id");
+                const expanded = !((queryId in that._queryId2Expanded) && that._queryId2Expanded[queryId]);
+                pre.text(that._query2text(queryId, expanded));
+                that._queryId2Expanded[queryId] = expanded;
+            };
+            tbody.find("button.copy-query").click(copyQueryToClipboard);
+            tbody.find("td.query_toggler").click(toggleQueryDisplay);
+        }
+        _query2text(queryId, expanded) {
+            return Common.query2text(this._id2query[queryId], expanded);
+        }
+    }
+    return QservWorkerMySQLQueries;
+});

From a502f1525692213e19856ee2b2cfc26d59161c36 Mon Sep 17 00:00:00 2001
From: Igor Gaponenko 
Date: Mon, 14 Aug 2023 18:28:05 +0000
Subject: [PATCH 10/12] Capture MySQL thread identifiers for worker queries

The identifiers are captured for the worker task monitoring
purposes. They are used by the Web Dashboard for associating task
activities with the corresponding MySQL queries issued by the tasks.
The task monitoring page of the Dashboard has been extended to display
a value of the identifier (if it was set for the task).
---
 src/mysql/MySqlConnection.cc         | 1 +
 src/mysql/MySqlConnection.h          | 3 +++
 src/wbase/Task.cc                    | 1 +
 src/wbase/Task.h                     | 7 +++++++
 src/wdb/QueryRunner.cc               | 1 +
 src/www/qserv/js/QservWorkerTasks.js | 2 ++
 6 files changed, 15 insertions(+)

diff --git a/src/mysql/MySqlConnection.cc b/src/mysql/MySqlConnection.cc
index a49a203213..4bdfa3980c 100644
--- a/src/mysql/MySqlConnection.cc
+++ b/src/mysql/MySqlConnection.cc
@@ -236,6 +236,7 @@ MYSQL* MySqlConnection::_connectHelper() {
         mysql_close(m);
         return c;
     }
+    _threadId = mysql_thread_id(m);
     return m;
 }
 
diff --git a/src/mysql/MySqlConnection.h b/src/mysql/MySqlConnection.h
index b1c8001531..5263d55cd3 100644
--- a/src/mysql/MySqlConnection.h
+++ b/src/mysql/MySqlConnection.h
@@ -65,6 +65,8 @@ class MySqlConnection : boost::noncopyable {
     static bool checkConnection(mysql::MySqlConfig const& mysqlconfig);
 
     bool connected() const { return _isConnected; }
+    unsigned long threadId() const { return _threadId; }
+
     // instance destruction invalidates this return value
     MYSQL* getMySql() { return _mysql; }
     MySqlConfig const& getMySqlConfig() const { return *_sqlConfig; }
@@ -104,6 +106,7 @@ class MySqlConnection : boost::noncopyable {
     MYSQL* _mysql;
     MYSQL_RES* _mysql_res;
     bool _isConnected;
+    unsigned long _threadId = 0;  ///< 0 if not connected
     std::shared_ptr _sqlConfig;
     bool _isExecuting;  ///< true during mysql_real_query and mysql_use_result
     bool _interrupted;  ///< true if cancellation requested
diff --git a/src/wbase/Task.cc b/src/wbase/Task.cc
index 1bca050aa2..de66409187 100644
--- a/src/wbase/Task.cc
+++ b/src/wbase/Task.cc
@@ -486,6 +486,7 @@ nlohmann::json Task::getJson() const {
     js["queryTime_msec"] = util::TimeUtils::tp2ms(_queryTime);
     js["finishTime_msec"] = util::TimeUtils::tp2ms(_finishTime);
     js["sizeSoFar"] = _totalSize;
+    js["mysqlThreadId"] = _mysqlThreadId.load();
     return js;
 }
 
diff --git a/src/wbase/Task.h b/src/wbase/Task.h
index f68d6622a9..849714b2cd 100644
--- a/src/wbase/Task.h
+++ b/src/wbase/Task.h
@@ -282,6 +282,11 @@ class Task : public util::CommandForThreadPool {
     /// Return a reference to the list of subchunk ids.
     const IntVector& getSubchunksVect() const { return _dbTblsAndSubchunks->subchunksVect; }
 
+    /// Set MySQL thread associated with a MySQL connection open before executing
+    /// task's queries. The identifier is sampled by the worker tasks monitoring
+    /// system in order to see what MySQL queries are being executed by tasks.
+    void setMySqlThreadId(unsigned long id) { _mysqlThreadId.store(id); }
+
 private:
     std::shared_ptr _userQueryInfo;  ///< Details common to Tasks in this UserQuery.
     std::shared_ptr _sendChannel;    ///< Send channel.
@@ -339,6 +344,8 @@ class Task : public util::CommandForThreadPool {
 
     /// Stores information on the query's resource usage.
     std::weak_ptr _queryStats;
+
+    std::atomic _mysqlThreadId{0};  ///< 0 if not connected to MySQL
 };
 
 }  // namespace lsst::qserv::wbase
diff --git a/src/wdb/QueryRunner.cc b/src/wdb/QueryRunner.cc
index 9114b585b9..60cc11317e 100644
--- a/src/wdb/QueryRunner.cc
+++ b/src/wdb/QueryRunner.cc
@@ -121,6 +121,7 @@ bool QueryRunner::_initConnection() {
         _multiError.push_back(error);
         return false;
     }
+    _task->setMySqlThreadId(_mysqlConn->threadId());
     return true;
 }
 
diff --git a/src/www/qserv/js/QservWorkerTasks.js b/src/www/qserv/js/QservWorkerTasks.js
index acd5a483d8..5dd167afca 100644
--- a/src/www/qserv/js/QservWorkerTasks.js
+++ b/src/www/qserv/js/QservWorkerTasks.js
@@ -136,6 +136,7 @@ function(CSSLoader,
           queried
           
           finished
+          MySQL thrd
             
       
       Loading...
@@ -331,6 +332,7 @@ function(CSSLoader,
   
${task.queryTime_msec ? QservWorkerTasks._timestamp2hhmmss(task.queryTime_msec) : ""}
${QservWorkerTasks._timestamps_diff2str(task.queryTime_msec, task.finishTime_msec, snapshotTime_msec)}
${task.finishTime_msec ? QservWorkerTasks._timestamp2hhmmss(task.finishTime_msec) : ""}
+
${task.mysqlThreadId || " "}
`; rowspan++; numTasksDisplayed++; From e22ca72c2c30ee44b7a9a448dbc46ab6214bbc54 Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Tue, 15 Aug 2023 00:12:31 +0000 Subject: [PATCH 11/12] Refined states of the worker tasks Introduced an additional state STARTED to differentiate between two events - when a task was started by a scheduler from the actual start time of the corresponding MySQL query. The change is meant to improve the monitoring of the worker tasks. The corresponding monitoring page of the Web Dashboard was modified as well. --- src/wbase/Task.cc | 12 +++++++++-- src/wbase/Task.h | 16 +++++++++------ src/wbase/TaskState.h | 13 +++++++++++- src/wdb/QueryRunner.cc | 1 + src/www/qserv/js/QservWorkerTasks.js | 30 ++++++++++++++++++---------- 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/wbase/Task.cc b/src/wbase/Task.cc index de66409187..c33a78f573 100644 --- a/src/wbase/Task.cc +++ b/src/wbase/Task.cc @@ -383,6 +383,7 @@ void Task::queued(std::chrono::system_clock::time_point const& now) { bool Task::isRunning() const { std::lock_guard lock(_stateMtx); switch (_state) { + case TaskState::STARTED: case TaskState::EXECUTING_QUERY: case TaskState::READING_DATA: return true; @@ -391,13 +392,18 @@ bool Task::isRunning() const { } } -/// Set values associated with the Task being started. void Task::started(std::chrono::system_clock::time_point const& now) { std::lock_guard guard(_stateMtx); - _state = TaskState::EXECUTING_QUERY; + _state = TaskState::STARTED; _startTime = now; } +void Task::queryExecutionStarted() { + std::lock_guard guard(_stateMtx); + _state = TaskState::EXECUTING_QUERY; + _queryExecTime = std::chrono::system_clock::now(); +} + void Task::queried() { std::lock_guard guard(_stateMtx); _state = TaskState::READING_DATA; @@ -430,6 +436,7 @@ std::chrono::milliseconds Task::getRunTime() const { switch (_state) { case TaskState::FINISHED: return std::chrono::duration_cast(_finishTime - _startTime); + case TaskState::STARTED: case TaskState::EXECUTING_QUERY: case TaskState::READING_DATA: return std::chrono::duration_cast(std::chrono::system_clock::now() - @@ -483,6 +490,7 @@ nlohmann::json Task::getJson() const { js["createTime_msec"] = util::TimeUtils::tp2ms(_createTime); js["queueTime_msec"] = util::TimeUtils::tp2ms(_queueTime); js["startTime_msec"] = util::TimeUtils::tp2ms(_startTime); + js["queryExecTime_msec"] = util::TimeUtils::tp2ms(_queryExecTime); js["queryTime_msec"] = util::TimeUtils::tp2ms(_queryTime); js["finishTime_msec"] = util::TimeUtils::tp2ms(_finishTime); js["sizeSoFar"] = _totalSize; diff --git a/src/wbase/Task.h b/src/wbase/Task.h index 849714b2cd..2365a9db23 100644 --- a/src/wbase/Task.h +++ b/src/wbase/Task.h @@ -252,6 +252,9 @@ class Task : public util::CommandForThreadPool { void queued(std::chrono::system_clock::time_point const& now); void started(std::chrono::system_clock::time_point const& now); + /// The actual execution of the corresponding MySQL query (or queries) started. + void queryExecutionStarted(); + /// MySQL finished executing queries. void queried(); @@ -335,12 +338,13 @@ class Task : public util::CommandForThreadPool { mutable std::mutex _stateMtx; ///< Mutex to protect state related members _state, _???Time. std::atomic _state{TaskState::CREATED}; std::chrono::system_clock::time_point _createTime = - std::chrono::system_clock::now(); ///< task was created - std::chrono::system_clock::time_point _queueTime; ///< task was queued - std::chrono::system_clock::time_point _startTime; ///< task processing started - std::chrono::system_clock::time_point _queryTime; ///< MySQL finished executing queries - std::chrono::system_clock::time_point _finishTime; ///< data transmission to Czar fiished - size_t _totalSize = 0; ///< Total size of the result so far. + std::chrono::system_clock::now(); ///< task was created + std::chrono::system_clock::time_point _queueTime; ///< task was queued + std::chrono::system_clock::time_point _startTime; ///< task processing started + std::chrono::system_clock::time_point _queryExecTime; ///< query execution at MySQL started + std::chrono::system_clock::time_point _queryTime; ///< MySQL finished executing queries + std::chrono::system_clock::time_point _finishTime; ///< data transmission to Czar fiished + size_t _totalSize = 0; ///< Total size of the result so far. /// Stores information on the query's resource usage. std::weak_ptr _queryStats; diff --git a/src/wbase/TaskState.h b/src/wbase/TaskState.h index 6dae818964..e53e1ab988 100644 --- a/src/wbase/TaskState.h +++ b/src/wbase/TaskState.h @@ -42,7 +42,14 @@ namespace lsst::qserv::wbase { * introducing an additional (Protobuf) representation for those, or converting * the values to strings and vs. */ -enum class TaskState : std::uint64_t { CREATED = 0, QUEUED, EXECUTING_QUERY, READING_DATA, FINISHED }; +enum class TaskState : std::uint64_t { + CREATED = 0, + QUEUED, + STARTED, + EXECUTING_QUERY, + READING_DATA, + FINISHED +}; /// @return The string representation of the input state. /// @throw std::invalid_argument If the string can't be parsed into a valid state. @@ -52,6 +59,8 @@ inline std::string taskState2str(TaskState state) { return "CREATED"; case TaskState::QUEUED: return "QUEUED"; + case TaskState::STARTED: + return "STARTED"; case TaskState::EXECUTING_QUERY: return "EXECUTING_QUERY"; case TaskState::READING_DATA: @@ -71,6 +80,8 @@ inline TaskState str2taskState(std::string const& state) { return TaskState::CREATED; else if (state == "QUEUED") return TaskState::QUEUED; + else if (state == "STARTED") + return TaskState::STARTED; else if (state == "EXECUTING_QUERY") return TaskState::EXECUTING_QUERY; else if (state == "READING_DATA") diff --git a/src/wdb/QueryRunner.cc b/src/wdb/QueryRunner.cc index 60cc11317e..49fa1bc8e4 100644 --- a/src/wdb/QueryRunner.cc +++ b/src/wdb/QueryRunner.cc @@ -282,6 +282,7 @@ bool QueryRunner::_dispatchChannel() { string const& query = _task->getQueryString(); util::Timer primeT; primeT.start(); + _task->queryExecutionStarted(); MYSQL_RES* res = _primeResult(query); // This runs the SQL query, throws SqlErrorObj on failure. primeT.stop(); needToFreeRes = true; diff --git a/src/www/qserv/js/QservWorkerTasks.js b/src/www/qserv/js/QservWorkerTasks.js index 5dd167afca..7f3d552939 100644 --- a/src/www/qserv/js/QservWorkerTasks.js +++ b/src/www/qserv/js/QservWorkerTasks.js @@ -51,7 +51,7 @@ function(CSSLoader, -
+
@@ -62,15 +62,17 @@ function(CSSLoader,
-
+
@@ -133,6 +135,8 @@ function(CSSLoader, started + query started + queried finished @@ -150,7 +154,7 @@ function(CSSLoader, }); cont.find("button#reset-tasks-form").click(() => { this._set_query(''); - this._set_state("EXECUTING_QUERY,READING_DATA"); + this._set_state("STARTED,EXECUTING_QUERY,READING_DATA"); this._set_max_tasks(200); this._set_update_interval_sec(10); this._load(); @@ -328,7 +332,9 @@ function(CSSLoader,
${task.queueTime_msec ? QservWorkerTasks._timestamp2hhmmss(task.queueTime_msec) : ""}
${QservWorkerTasks._timestamps_diff2str(task.queueTime_msec, task.startTime_msec, snapshotTime_msec)}
${task.startTime_msec ? QservWorkerTasks._timestamp2hhmmss(task.startTime_msec) : ""}
-
${QservWorkerTasks._timestamps_diff2str(task.startTime_msec, task.queryTime_msec, snapshotTime_msec)}
+
${QservWorkerTasks._timestamps_diff2str(task.startTime_msec, task.queryExecTime_msec, snapshotTime_msec)}
+
${task.queryExecTime_msec ? QservWorkerTasks._timestamp2hhmmss(task.queryExecTime_msec) : ""}
+
${QservWorkerTasks._timestamps_diff2str(task.queryExecTime_msec, task.queryTime_msec, snapshotTime_msec)}
${task.queryTime_msec ? QservWorkerTasks._timestamp2hhmmss(task.queryTime_msec) : ""}
${QservWorkerTasks._timestamps_diff2str(task.queryTime_msec, task.finishTime_msec, snapshotTime_msec)}
${task.finishTime_msec ? QservWorkerTasks._timestamp2hhmmss(task.finishTime_msec) : ""}
@@ -362,9 +368,10 @@ function(CSSLoader, switch (state) { case 0: return 'table-warning'; case 1: return 'table-light'; - case 2: return 'table-primary'; - case 3: return 'table-info'; - case 4: return 'table-secondary'; + case 2: return 'table-danger'; + case 3: return 'table-primary'; + case 4: return 'table-info'; + case 5: return 'table-secondary'; default: return 'table-warning'; } } @@ -372,9 +379,10 @@ function(CSSLoader, switch (state) { case 0: return 'CREATED'; case 1: return 'QUEUED'; - case 2: return 'EXECUTING_QUERY'; - case 3: return 'READING_DATA'; - case 4: return 'FINISHED'; + case 2: return 'STARTED'; + case 3: return 'EXECUTING_QUERY'; + case 4: return 'READING_DATA'; + case 5: return 'FINISHED'; default: return 'UNKNOWN'; } } From cefbb18e20e99f905c6a59186227c884e46744b9 Mon Sep 17 00:00:00 2001 From: Igor Gaponenko Date: Tue, 15 Aug 2023 02:13:52 +0000 Subject: [PATCH 12/12] Web Dashboard: improved layout of the worker task histos --- src/www/qserv/js/QservWorkerTaskHist.js | 130 ++++++++++++++---------- 1 file changed, 78 insertions(+), 52 deletions(-) diff --git a/src/www/qserv/js/QservWorkerTaskHist.js b/src/www/qserv/js/QservWorkerTaskHist.js index f7a5c8826a..6782750e0d 100644 --- a/src/www/qserv/js/QservWorkerTaskHist.js +++ b/src/www/qserv/js/QservWorkerTaskHist.js @@ -47,15 +47,9 @@ function(CSSLoader, 'rowsPerTask' ]; static _table_head(histogram) { - if (_.isUndefined(histogram)) { - return ` - - worker -`; - } + if (_.isUndefined(histogram)) return ''; let html = ` - worker QID total @@ -78,6 +72,11 @@ function(CSSLoader,
+
+ + +
@@ -147,10 +146,20 @@ function(CSSLoader, _set_histogram_name(val) { this._form_control('select', 'histogram-name').val(val); } _update_interval_sec() { return this._form_control('select', 'update-interval').val(); } _set_update_interval_sec(val) { this._form_control('select', 'update-interval').val(val); } - - /** - * Table for displaying histograms that are being produced at workers. - */ + _worker() { return this._form_control('select', 'worker').val(); } + _set_worker(val) { this._form_control('select', 'worker').val(val); } + _set_workers(workers) { + const prev_worker = this._worker(); + let html = ''; + for (let i in workers) { + const worker = workers[i]; + const selected = (_.isEmpty(prev_worker) && (i === 0)) || + (!_.isEmpty(prev_worker) && (prev_worker === worker)); + html += ` + `; + } + this._form_control('select', 'worker').html(html); + } _table() { if (this._table_obj === undefined) { this._table_obj = this.fwk_app_container.find('table#fwk-qserv-task-hist'); @@ -162,18 +171,41 @@ function(CSSLoader, * Load data from a web service then render it to the application's page. */ _load() { - if (this._loading === undefined) this._loading = false; - if (this._loading) return; - this._loading = true; - - this._table().children('caption').addClass('updating'); - + if (this._loading === undefined) this._loading = false; + if (this._loading) return; + this._loading = true; + this._table().children('caption').addClass('updating'); + Fwk.web_service_GET( + "/replication/config", + {version: Common.RestAPIVersion}, + (data) => { + let workers = []; + for (let i in data.config.workers) { + workers.push(data.config.workers[i].name); + } + this._set_workers(workers); + this._load_histograms(); + }, + (msg) => { + console.log('request failed', this.fwk_app_name, msg); + this._table().children('caption').html('No Response'); + this._table().children('caption').removeClass('updating'); + this._loading = false; + } + ); + } + _load_histograms() { Fwk.web_service_GET( - "/replication/qserv/worker/status", + "/replication/qserv/worker/status/" + this._worker(), {timeout_sec: 2, version: Common.RestAPIVersion}, (data) => { - this._display(data.status); - Fwk.setLastUpdate(this._table().children('caption')); + if (data.success) { + this._display(data.status); + Fwk.setLastUpdate(this._table().children('caption')); + } else { + console.log('request failed', this.fwk_app_name, data.error); + this._table().children('caption').html('' + data.error + ''); + } this._table().children('caption').removeClass('updating'); this._loading = false; }, @@ -190,31 +222,29 @@ function(CSSLoader, * Display histograms */ _display(data) { - const queryInspectTitle = "Click to see detailed info (progress, messages, etc.) on the query."; - const qid = this._qid(); - const histogram_name = this._histogram_name(); let thead_html = QservWorkerTaskHist._table_head(); let tbody_html = ''; - for (let worker in data) { - if (!data[worker].success || _.isUndefined(data[worker].info.processor) || - _.isUndefined(data[worker].info.processor.queries) || - _.isUndefined(data[worker].info.processor.queries.query_stats)) { - continue; - } + const worker = this._worker(); + if (!data[worker].success || _.isUndefined(data[worker].info.processor) || + _.isUndefined(data[worker].info.processor.queries) || + _.isUndefined(data[worker].info.processor.queries.query_stats)) { + ; + } else { let query_stats = data[worker].info.processor.queries.query_stats; - if (_.isEmpty(query_stats)) continue; - let rowspan = 1; - let html = ''; - for (let queryId in query_stats) { - if (!_.isEmpty(qid) && (qid !== queryId)) continue; - if (!_.has(query_stats[queryId], "histograms")) continue; - let histograms = query_stats[queryId].histograms; - if (!_.has(histograms, histogram_name)) continue; - let histogram = histograms[histogram_name]; - if (_.isEmpty(html)) { - thead_html = QservWorkerTaskHist._table_head(histogram); - } - html += ` + if (!_.isEmpty(query_stats)) { + const queryInspectTitle = "Click to see detailed info (progress, messages, etc.) on the query."; + const qid = this._qid(); + const histogram_name = this._histogram_name(); + for (let queryId in query_stats) { + if (!_.isEmpty(qid) && (qid !== queryId)) continue; + if (!_.has(query_stats[queryId], "histograms")) continue; + let histograms = query_stats[queryId].histograms; + if (!_.has(histograms, histogram_name)) continue; + let histogram = histograms[histogram_name]; + if (_.isEmpty(thead_html)) { + thead_html = QservWorkerTaskHist._table_head(histogram); + } + tbody_html += `
${queryId}
@@ -223,19 +253,15 @@ function(CSSLoader,
${histogram.total ? histogram.total.toFixed(3) : ''}
${histogram.totalCount ? histogram.totalCount : ''}
${histogram.avg ? histogram.avg.toFixed(3) : ''}
`; - for (let i in histogram.buckets) { - let bucket = histogram.buckets[i]; - html += ` + for (let i in histogram.buckets) { + let bucket = histogram.buckets[i]; + tbody_html += `
${bucket.count ? bucket.count : ''}
`; - } - html += ` + } + tbody_html += ` `; -rowspan++; + } } - tbody_html += ` - - ${worker} -` + html; } this._table().children('thead').html(thead_html); let tbody = this._table().children('tbody').html(tbody_html);