diff --git a/docs/references/http_api_reference.mdx b/docs/references/http_api_reference.mdx index 003ebb31eb..a93c8ee260 100644 --- a/docs/references/http_api_reference.mdx +++ b/docs/references/http_api_reference.mdx @@ -2370,4 +2370,168 @@ A `500` HTTP status code indicates an error condition. The response includes a J ---- \ No newline at end of file +--- + +## Show buffer + +**GET** `/instance/buffer` + +List the cached buffer objects in database. + +### Request + +- Method: GET +- URL: `/instance/buffer` +- Headers: `accept: application/json` + +#### Request example + +```shell +curl --request GET \ + --url http://localhost:23820/instance/buffer \ + --header 'accept: application/json' +``` + +### Response + + + + +The response includes a JSON object like the following: + +```shell +{ + "buffer": [ + { + "buffered_type": "Persistent", + "path": "/var/infinity/data/nlAn5spku9_db_default_db/sNzUhKy3er_table_table1/seg_0/blk_0/0.col", + "size": "32768", + "status": "Freed", + "type": "data" + }, + { + "buffered_type": "Persistent", + "path": "/var/infinity/data/nlAn5spku9_db_default_db/sNzUhKy3er_table_table1/seg_0/blk_0/2.col", + "size": "131072", + "status": "Freed", + "type": "data" + }, + { + "buffered_type": "Persistent", + "path": "/var/infinity/data/nlAn5spku9_db_default_db/sNzUhKy3er_table_table1/seg_0/blk_0/version", + "size": "65536", + "status": "Freed", + "type": "version data" + } + ], + "error_code": 0 +} +``` + +- `"error_code"`: `integer` + `0`: The operation succeeds. + + + + +A `500` HTTP status code indicates an error condition. The response includes a JSON object like the following: + +```shell +{ + "error_code": 2005, + "error_message": "Not support in maintenance mode" +} +``` + +- `"error_code"`: `integer` + A non-zero value indicates a specific error condition. +- `"error_message"`: `string` + When `error_code` is non-zero, `"error_message"` provides additional details about the error. + + + + +--- + +## Show profiles + +**GET** `/instance/profiles` + +When set session variable 'enable_profiling', Infinity will record query profile information. This command is to list recorded queries profile. + +### Request + +- Method: GET +- URL: `/instance/profiles` +- Headers: `accept: application/json` + +#### Request example + +```shell +curl --request GET \ + --url http://localhost:23820/instance/profiles \ + --header 'accept: application/json' +``` + +### Response + + + + +The response includes a JSON object like the following: + +```shell +{ + "error_code": 0, + "profiles": [ + { + "command parsing": "250us", + "commit": "0ns", + "execution": "380us", + "logical plan building": "133us", + "physical plan building": "44us", + "pipeline building": "38us", + "plan optimizing": "1000ns", + "record_no": "0", + "rollback": "35us", + "task building": "70us", + "total_cost": "951us" + } + ] +} +``` + +- `"error_code"`: `integer` + `0`: The operation succeeds. + + + + +A `500` HTTP status code indicates an error condition. The response includes a JSON object like the following: + +```shell +{ + "error_code": 2005, + "error_message": "Not support in maintenance mode" +} +``` + +- `"error_code"`: `integer` + A non-zero value indicates a specific error condition. +- `"error_message"`: `string` + When `error_code` is non-zero, `"error_message"` provides additional details about the error. + + + + +--- diff --git a/example/http/show_metrics.sh b/example/http/show_metrics.sh new file mode 100755 index 0000000000..57fdb47f9b --- /dev/null +++ b/example/http/show_metrics.sh @@ -0,0 +1,38 @@ +# Copyright(C) 2024 InfiniFlow, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# show buffer +echo -e '\n-- show buffer' +curl --request GET \ + --url http://localhost:23820/instance/buffer \ + --header 'accept: application/json' + +# show profiles +echo -e '\n\n-- show profiles' +curl --request GET \ + --url http://localhost:23820/instance/profiles \ + --header 'accept: application/json' + +# show memindex +echo -e '\n\n-- show memindex' +curl --request GET \ + --url http://localhost:23820/instance/memindex \ + --header 'accept: application/json' + +# show queries +echo -e '\n\n-- show queries' +curl --request GET \ + --url http://localhost:23820/instance/queries \ + --header 'accept: application/json' + diff --git a/src/network/http_server.cpp b/src/network/http_server.cpp index f4adcc4283..bca7899be8 100644 --- a/src/network/http_server.cpp +++ b/src/network/http_server.cpp @@ -3313,6 +3313,162 @@ class SetConfigHandler final : public HttpRequestHandler { } }; +class ShowBufferHandler final : public HttpRequestHandler { +public: + SharedPtr handle(const SharedPtr &request) final { + auto infinity = Infinity::RemoteConnect(); + DeferFn defer_fn([&]() { infinity->RemoteDisconnect(); }); + + nlohmann::json json_response; + HTTPStatus http_status; + QueryResult result = infinity->Query("show buffer"); + + if (result.IsOk()) { + SizeT block_rows = result.result_table_->DataBlockCount(); + for (SizeT block_id = 0; block_id < block_rows; ++block_id) { + DataBlock *data_block = result.result_table_->GetDataBlockById(block_id).get(); + auto row_count = data_block->row_count(); + auto column_cnt = result.result_table_->ColumnCount(); + for (int row = 0; row < row_count; ++row) { + nlohmann::json json_table; + for (SizeT col = 0; col < column_cnt; ++col) { + const String &column_name = result.result_table_->GetColumnNameById(col); + Value value = data_block->GetValue(col, row); + const String &column_value = value.ToString(); + json_table[column_name] = column_value; + } + json_response["buffer"].push_back(json_table); + } + } + json_response["error_code"] = 0; + http_status = HTTPStatus::CODE_200; + } else { + json_response["error_code"] = result.ErrorCode(); + json_response["error_message"] = result.ErrorMsg(); + http_status = HTTPStatus::CODE_500; + } + + return ResponseFactory::createResponse(http_status, json_response.dump()); + } +}; + +class ShowProfilesHandler final : public HttpRequestHandler { +public: + SharedPtr handle(const SharedPtr &request) final { + auto infinity = Infinity::RemoteConnect(); + DeferFn defer_fn([&]() { infinity->RemoteDisconnect(); }); + + nlohmann::json json_response; + HTTPStatus http_status; + QueryResult result = infinity->Query("show profiles"); + + if (result.IsOk()) { + SizeT block_rows = result.result_table_->DataBlockCount(); + for (SizeT block_id = 0; block_id < block_rows; ++block_id) { + DataBlock *data_block = result.result_table_->GetDataBlockById(block_id).get(); + auto row_count = data_block->row_count(); + auto column_cnt = result.result_table_->ColumnCount(); + for (int row = 0; row < row_count; ++row) { + nlohmann::json json_table; + for (SizeT col = 0; col < column_cnt; ++col) { + const String &column_name = result.result_table_->GetColumnNameById(col); + Value value = data_block->GetValue(col, row); + const String &column_value = value.ToString(); + json_table[column_name] = column_value; + } + json_response["profiles"].push_back(json_table); + } + } + json_response["error_code"] = 0; + http_status = HTTPStatus::CODE_200; + } else { + json_response["error_code"] = result.ErrorCode(); + json_response["error_message"] = result.ErrorMsg(); + http_status = HTTPStatus::CODE_500; + } + + return ResponseFactory::createResponse(http_status, json_response.dump()); + } +}; + +class ShowMemIndexHandler final : public HttpRequestHandler { +public: + SharedPtr handle(const SharedPtr &request) final { + auto infinity = Infinity::RemoteConnect(); + DeferFn defer_fn([&]() { infinity->RemoteDisconnect(); }); + + nlohmann::json json_response; + HTTPStatus http_status; + QueryResult result = infinity->Query("show memindex"); + + if (result.IsOk()) { + SizeT block_rows = result.result_table_->DataBlockCount(); + for (SizeT block_id = 0; block_id < block_rows; ++block_id) { + DataBlock *data_block = result.result_table_->GetDataBlockById(block_id).get(); + auto row_count = data_block->row_count(); + auto column_cnt = result.result_table_->ColumnCount(); + for (int row = 0; row < row_count; ++row) { + nlohmann::json json_table; + for (SizeT col = 0; col < column_cnt; ++col) { + const String &column_name = result.result_table_->GetColumnNameById(col); + Value value = data_block->GetValue(col, row); + const String &column_value = value.ToString(); + json_table[column_name] = column_value; + } + json_response["index"].push_back(json_table); + } + } + json_response["error_code"] = 0; + http_status = HTTPStatus::CODE_200; + } else { + json_response["error_code"] = result.ErrorCode(); + json_response["error_message"] = result.ErrorMsg(); + http_status = HTTPStatus::CODE_500; + } + + return ResponseFactory::createResponse(http_status, json_response.dump()); + } +}; + +class ShowQueriesHandler final : public HttpRequestHandler { +public: + SharedPtr handle(const SharedPtr &request) final { + auto infinity = Infinity::RemoteConnect(); + DeferFn defer_fn([&]() { infinity->RemoteDisconnect(); }); + + nlohmann::json json_response; + HTTPStatus http_status; + QueryResult result = infinity->Query("show queries"); + + if (result.IsOk()) { + SizeT block_rows = result.result_table_->DataBlockCount(); + for (SizeT block_id = 0; block_id < block_rows; ++block_id) { + DataBlock *data_block = result.result_table_->GetDataBlockById(block_id).get(); + auto row_count = data_block->row_count(); + auto column_cnt = result.result_table_->ColumnCount(); + for (int row = 0; row < row_count; ++row) { + nlohmann::json json_table; + for (SizeT col = 0; col < column_cnt; ++col) { + const String &column_name = result.result_table_->GetColumnNameById(col); + Value value = data_block->GetValue(col, row); + const String &column_value = value.ToString(); + json_table[column_name] = column_value; + } + json_response["queries"].push_back(json_table); + } + } + json_response["error_code"] = 0; + http_status = HTTPStatus::CODE_200; + } else { + json_response["error_code"] = result.ErrorCode(); + json_response["error_message"] = result.ErrorMsg(); + http_status = HTTPStatus::CODE_500; + } + + return ResponseFactory::createResponse(http_status, json_response.dump()); + } +}; + class ShowCurrentNodeHandler final : public HttpRequestHandler { public: SharedPtr handle(const SharedPtr &request) final { @@ -3449,6 +3605,12 @@ void HTTPServer::Start(const String& ip_address, u16 port) { router->route("POST", "/configs", MakeShared()); + // metrics + router->route("GET", "/instance/buffer", MakeShared()); + router->route("GET", "/instance/profiles", MakeShared()); + router->route("GET", "/instance/memindex", MakeShared()); + router->route("GET", "/instance/queries", MakeShared()); + // variable router->route("GET", "/variables/global", MakeShared()); router->route("GET", "/variables/global/{variable_name}", MakeShared());