This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit e9c19c87ce2e731ead465962991b533e648fa316 Author: Chris McFarlen <[email protected]> AuthorDate: Wed Jul 2 09:44:31 2025 -0500 Expose JSON client timeout to CLI (#12323) * Expose JSON client timeout to CLI * Make default timeout much larger (cherry picked from commit 624462f3a94e85093dc2207d3db84cf9ad1db029) --- include/shared/rpc/RPCClient.h | 10 +++++----- src/traffic_ctl/CtrlCommands.cc | 31 +++++++++++++++++++++++++------ src/traffic_ctl/CtrlCommands.h | 25 +++++++++++++++---------- src/traffic_ctl/FileConfigCommand.h | 3 +-- src/traffic_ctl/traffic_ctl.cc | 4 +++- src/traffic_top/stats.h | 3 ++- 6 files changed, 51 insertions(+), 25 deletions(-) diff --git a/include/shared/rpc/RPCClient.h b/include/shared/rpc/RPCClient.h index 57c629f1aa..a9b9453543 100644 --- a/include/shared/rpc/RPCClient.h +++ b/include/shared/rpc/RPCClient.h @@ -21,7 +21,7 @@ /// JSONRPC 2.0 RPC network client. -#include <iostream> +#include <chrono> #include <string_view> #include <yaml-cpp/yaml.h> @@ -48,7 +48,7 @@ public: /// endode/decode you can just call @c invoke(JSONRPCRequest const &req). /// @throw runtime_error std::string - invoke(std::string_view req) + invoke(std::string_view req, std::chrono::milliseconds timeout_ms, int attempts) { std::string err_text; // for error messages. try { @@ -56,7 +56,7 @@ public: if (!_client.is_closed()) { std::string resp; _client.send(req); - switch (_client.read_all(resp)) { + switch (_client.read_all(resp, timeout_ms, attempts)) { case IPCSocketClient::ReadStatus::NO_ERROR: { _client.disconnect(); return resp; @@ -98,13 +98,13 @@ public: /// @throw YAML::Exception template <typename Codec = yamlcpp_json_emitter> JSONRPCResponse - invoke(JSONRPCRequest const &req) + invoke(JSONRPCRequest const &req, std::chrono::milliseconds timeout_ms, int attempts) { static_assert(internal::has_decode<Codec>::value || internal::has_encode<Codec>::value, "You need to implement encode/decode in your own codec impl."); // We should add a static_assert and make sure encode/decode are part of Codec type. auto const &reqStr = Codec::encode(req); - return Codec::decode(invoke(reqStr)); + return Codec::decode(invoke(reqStr, timeout_ms, attempts)); } private: diff --git a/src/traffic_ctl/CtrlCommands.cc b/src/traffic_ctl/CtrlCommands.cc index 31fb78cf82..17efd4439d 100644 --- a/src/traffic_ctl/CtrlCommands.cc +++ b/src/traffic_ctl/CtrlCommands.cc @@ -85,14 +85,14 @@ CtrlCommand::execute() } std::string -RPCAccessor::invoke_rpc(std::string const &request) +RPCAccessor::invoke_rpc(std::string const &request, std::chrono::milliseconds timeout_ms, int attempts) { if (_printer->print_rpc_message()) { std::string text; swoc::bwprint(text, "--> {}", request); _printer->write_debug(std::string_view{text}); } - if (auto resp = _rpcClient.invoke(request); !resp.empty()) { + if (auto resp = _rpcClient.invoke(request, timeout_ms, attempts); !resp.empty()) { // all good. if (_printer->print_rpc_message()) { std::string text; @@ -106,18 +106,19 @@ RPCAccessor::invoke_rpc(std::string const &request) } shared::rpc::JSONRPCResponse -RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request) +RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request, std::chrono::milliseconds timeout_ms, int attempts) { std::string encodedRequest = Codec::encode(request); - std::string resp = invoke_rpc(encodedRequest); + std::string resp = invoke_rpc(encodedRequest, timeout_ms, attempts); return Codec::decode(resp); } void -RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request, std::string &resp) +RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request, std::string &resp, std::chrono::milliseconds timeout_ms, + int attempts) { std::string encodedRequest = Codec::encode(request); - resp = invoke_rpc(encodedRequest); + resp = invoke_rpc(encodedRequest, timeout_ms, attempts); } // ----------------------------------------------------------------------------------------------------------------------------------- ConfigCommand::ConfigCommand(ts::Arguments *args) : RecordCommand(args) @@ -166,6 +167,24 @@ RecordCommand::record_fetch(ts::ArgumentData argData, bool isRegex, RecordQueryT return invoke_rpc(request); } +std::string +CtrlCommand::invoke_rpc(std::string const &request) +{ + auto timeout = std::chrono::milliseconds(std::stoi(get_parsed_arguments()->get("read-timeout").value())); + auto attempts = std::stoi(get_parsed_arguments()->get("read-attempts").value()); + + return RPCAccessor::invoke_rpc(request, timeout, attempts); +} + +shared::rpc::JSONRPCResponse +CtrlCommand::invoke_rpc(shared::rpc::ClientRequest const &request) +{ + auto timeout = std::chrono::milliseconds(std::stoi(get_parsed_arguments()->get("read-timeout").value())); + auto attempts = std::stoi(get_parsed_arguments()->get("read-attempts").value()); + + return RPCAccessor::invoke_rpc(request, timeout, attempts); +} + void ConfigCommand::config_match() { diff --git a/src/traffic_ctl/CtrlCommands.h b/src/traffic_ctl/CtrlCommands.h index e20d70d8dd..cc3a6ad8fa 100644 --- a/src/traffic_ctl/CtrlCommands.h +++ b/src/traffic_ctl/CtrlCommands.h @@ -34,16 +34,17 @@ protected: /// @param request A string representation of the json/yaml request. /// @return a string with the json/yaml response. /// @note This function does print the raw string if requested by the "--format". No printer involved, standard output. - std::string invoke_rpc(std::string const &request); + std::string invoke_rpc(std::string const &request, std::chrono::milliseconds timeout_ms, int attempts); /// @brief Function that calls the rpc server. This function takes a json objects and uses the defined coded to convert them to a /// string. This function will call invoke_rpc(string) overload. /// @param A Client request. /// @return A server response. - shared::rpc::JSONRPCResponse invoke_rpc(shared::rpc::ClientRequest const &request); + shared::rpc::JSONRPCResponse invoke_rpc(shared::rpc::ClientRequest const &request, std::chrono::milliseconds timeout_ms, + int attempts); /// @brief Function that calls the rpc server. The response will not be decoded, it will be a raw string. - void invoke_rpc(shared::rpc::ClientRequest const &request, std::string &bw); + void invoke_rpc(shared::rpc::ClientRequest const &request, std::string &bw, std::chrono::milliseconds timeout_ms, int attempts); std::unique_ptr<BasePrinter> _printer; //!< Specific output formatter. This should be created by the derived class. private: @@ -56,7 +57,7 @@ private: /// This class should be used as a base class for every new command or group of commands that are related. /// The base class will provide the client communication through the @c invoke_call member function. Arguments that were /// parsed by the traffic_ctl are available as a member to all the derived classes. -class CtrlCommand +class CtrlCommand : public RPCAccessor { public: virtual ~CtrlCommand() = default; @@ -76,6 +77,10 @@ public: /// should be set when the signal is handled. static std::atomic_int Signal_Flagged; + std::string invoke_rpc(std::string const &request); + + shared::rpc::JSONRPCResponse invoke_rpc(shared::rpc::ClientRequest const &request); + protected: /// @brief The whole design is that the command will execute the @c _invoked_func once invoked. This function ptr should be /// set by the appropriated derived class base on the passed parameters. The derived class have the option to override @@ -98,7 +103,7 @@ private: /// @brief Record Command Implementation /// Used as base class for any command that needs to access to a TS record. /// If deriving from this class, make sure you implement @c execute_subcommand() and call the _invoked_func yourself. -class RecordCommand : public CtrlCommand, public RPCAccessor +class RecordCommand : public CtrlCommand { public: using CtrlCommand::CtrlCommand; @@ -157,7 +162,7 @@ public: MetricCommand(ts::Arguments *args); }; // ----------------------------------------------------------------------------------------------------------------------------------- -class HostCommand : public CtrlCommand, public RPCAccessor +class HostCommand : public CtrlCommand { public: HostCommand(ts::Arguments *args); @@ -173,7 +178,7 @@ private: void status_up(); }; // ----------------------------------------------------------------------------------------------------------------------------------- -class PluginCommand : public CtrlCommand, public RPCAccessor +class PluginCommand : public CtrlCommand { public: PluginCommand(ts::Arguments *args); @@ -183,7 +188,7 @@ private: void plugin_msg(); }; // ----------------------------------------------------------------------------------------------------------------------------------- -class DirectRPCCommand : public CtrlCommand, public RPCAccessor +class DirectRPCCommand : public CtrlCommand { public: DirectRPCCommand(ts::Arguments *args); @@ -204,7 +209,7 @@ private: bool validate_input(std::string const &in) const; }; // ----------------------------------------------------------------------------------------------------------------------------------- -class ServerCommand : public CtrlCommand, public RPCAccessor +class ServerCommand : public CtrlCommand { public: ServerCommand(ts::Arguments *args); @@ -228,7 +233,7 @@ private: }; // // ----------------------------------------------------------------------------------------------------------------------------------- -struct StorageCommand : public CtrlCommand, public RPCAccessor { +struct StorageCommand : public CtrlCommand { StorageCommand(ts::Arguments *args); private: diff --git a/src/traffic_ctl/FileConfigCommand.h b/src/traffic_ctl/FileConfigCommand.h index 9f4c3bb03c..e1eab75293 100644 --- a/src/traffic_ctl/FileConfigCommand.h +++ b/src/traffic_ctl/FileConfigCommand.h @@ -67,8 +67,7 @@ struct FlatYAMLAccessor { } protected: - std::vector<YAML::Node> _docs; - std::unique_ptr<BasePrinter> _printer; //!< Specific output formatter. This should be created by the derived class. + std::vector<YAML::Node> _docs; }; /// @brief Class used to deal with the config file changes. Append or modify an existing records.yaml field diff --git a/src/traffic_ctl/traffic_ctl.cc b/src/traffic_ctl/traffic_ctl.cc index 28e64f0ab3..33100cc01e 100644 --- a/src/traffic_ctl/traffic_ctl.cc +++ b/src/traffic_ctl/traffic_ctl.cc @@ -78,7 +78,9 @@ main([[maybe_unused]] int argc, const char **argv) .add_option("--version", "-V", "Print version string") .add_option("--help", "-h", "Print usage information") .add_option("--run-root", "", "using TS_RUNROOT as sandbox", "TS_RUNROOT", 1) - .add_option("--format", "-f", "Use a specific output format {json|rpc}", "", 1, "", "format"); + .add_option("--format", "-f", "Use a specific output format {json|rpc}", "", 1, "", "format") + .add_option("--read-timeout-ms", "", "Read timeout for RPC (in milliseconds)", "", 1, "10000", "read-timeout") + .add_option("--read-attempts", "", "Read attempts for RPC", "", 1, "100", "read-attempts"); auto &config_command = parser.add_command("config", "Manipulate configuration records").require_commands(); auto &metric_command = parser.add_command("metric", "Manipulate performance metrics").require_commands(); diff --git a/src/traffic_top/stats.h b/src/traffic_top/stats.h index cfed64fbb4..3bcccf5fd9 100644 --- a/src/traffic_top/stats.h +++ b/src/traffic_top/stats.h @@ -22,6 +22,7 @@ */ #pragma once +#include <chrono> #include <map> #include <string> #include <sys/types.h> @@ -495,7 +496,7 @@ private: rpc::RPCClient rpcClient; // invoke the rpc. - auto const &rpcResponse = rpcClient.invoke<>(request); + auto const &rpcResponse = rpcClient.invoke<>(request, std::chrono::milliseconds(1000), 10); if (!rpcResponse.is_error()) { auto const &records = rpcResponse.result.as<rpc::RecordLookUpResponse>();
