diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b5a077a342dac3a7572f798af64e19dbc1d7b116..14099db4f74f829307e9c2049417df9e9b338baf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,6 +61,7 @@ gkfs: -DGKFS_USE_GUIDED_DISTRIBUTION:BOOL=ON -DGKFS_ENABLE_PARALLAX:BOOL=ON -DGKFS_ENABLE_ROCKSDB:BOOL=ON + -DGKFS_CHUNK_STATS:BOOL=ON ${CI_PROJECT_DIR} - make -j$(nproc) install # reduce artifacts size diff --git a/CHANGELOG.md b/CHANGELOG.md index 05f997a69f40ace3a5131f9fb19824c51cd2e63a..1082251eb4a09ff22a16d7af92a6df5d7666ee41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Parallax ([!110](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/110)). - Added support to use multiple metadata backends. - Added `--clean-rootdir-finish` argument to remove rootdir/metadir at the end when the daemon finishes. +- Added Stats ([!128](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/128)) gathering in servers + - GKFS_CHUNK_STATS enables chunk usage output + - Stats output can be enabled with --output-stats ### Changed diff --git a/CMakeLists.txt b/CMakeLists.txt index 7589df5191bf2878d9c6db29e98e3f3e5b85cdae..84410f80e40d9c3679bf1f0ff263c15075f446f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,12 @@ if(GKFS_USE_GUIDED_DISTRIBUTION) message(STATUS "[gekkofs] Guided data distributor input file path: ${GKFS_USE_GUIDED_DISTRIBUTION_PATH}") endif() +option(GKFS_CHUNK_STATS "Gather Chunk Stats " OFF) +if (GKFS_CHUNK_STATS) + add_definitions(-DGKFS_CHUNK_STATS) +endif () +message(STATUS "[gekkofs] Gather Chunk Stats: ${GKFS_CHUNK_STATS}") + configure_file(include/common/cmake_configure.hpp.in include/common/cmake_configure.hpp) diff --git a/README.md b/README.md index 24857eba041ce9a1ac8c2a19f756208f2b5f1c10..93bcb4b623981be8306bdc0aacabe5d10c0a0f35 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ Options: RocksDB is default if not set. Parallax support is experimental. Note, parallaxdb creates a file called rocksdbx with 8GB created in metadir. --parallaxsize TEXT parallaxdb - metadata file size in GB (default 8GB), used only with new files + --output-stats TEXT Enables the output of the stats on the FILE (each 10s) for debug --version Print version and exit. ``` diff --git a/docs/sphinx/users/running.md b/docs/sphinx/users/running.md index 4750c151cd839d473e6b08cdfee4d7348cfcd4dc..2e169d0a00bc54a54e9d09a2a10e6515b89631b0 100644 --- a/docs/sphinx/users/running.md +++ b/docs/sphinx/users/running.md @@ -79,6 +79,7 @@ Options: RocksDB is default if not set. Parallax support is experimental. Note, parallaxdb creates a file called rocksdbx with 8GB created in metadir. --parallaxsize TEXT parallaxdb - metadata file size in GB (default 8GB), used only with new files + --output-stats TEXT Outputs the stats to the file each 10s. --version Print version and exit. ```` diff --git a/include/common/statistics/stats.hpp b/include/common/statistics/stats.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ba18799e4342242d616bc066fb94c7bb1e4616c5 --- /dev/null +++ b/include/common/statistics/stats.hpp @@ -0,0 +1,250 @@ +/* + Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This file is part of GekkoFS. + + GekkoFS 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. + + GekkoFS 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 GNU General Public License + along with GekkoFS. If not, see . + + SPDX-License-Identifier: GPL-3.0-or-later +*/ + +#ifndef GKFS_COMMON_STATS_HPP +#define GKFS_COMMON_STATS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/** + * Provides storage capabilities to provide stats about GekkoFS + * The information is per server. + * We do not provide accurate stats for 1-5-10 minute stats + * + */ +namespace gkfs::utils { + +/* + Number of operations (Create, write/ read, remove, mkdir...) + Size of database (metadata keys, should be not needed, any) + Size of data (+write - delete) + Server Bandwidth (write / read operations) + + mean, (lifetime of the server) + 1 minute mean + 5 minute mean + 10 minute mean + + To provide the stats that we need, + we need to store the info and the timestamp to calculate it + A vector should work, with a maximum of elements, + The stats will only be calculated when requested + a cached value will be send (with a deadline) + */ +class Stats { +public: + enum class IOPS_OP { + IOPS_CREATE, + IOPS_WRITE, + IOPS_READ, + IOPS_STATS, + IOPS_DIRENTS, + IOPS_REMOVE, + }; ///< enum storing IOPS Stats + + enum class SIZE_OP { WRITE_SIZE, READ_SIZE }; ///< enum storing Size Stats + +private: + constexpr static const std::initializer_list all_IOPS_OP = { + IOPS_OP::IOPS_CREATE, + IOPS_OP::IOPS_WRITE, + IOPS_OP::IOPS_READ, + IOPS_OP::IOPS_STATS, + IOPS_OP::IOPS_DIRENTS, + IOPS_OP::IOPS_REMOVE}; ///< Enum IOPS iterator + + constexpr static const std::initializer_list all_SIZE_OP = { + SIZE_OP::WRITE_SIZE, SIZE_OP::READ_SIZE}; ///< Enum SIZE iterator + + const std::vector IOPS_OP_S = { + "IOPS_CREATE", "IOPS_WRITE", "IOPS_READ", + "IOPS_STATS", "IOPS_DIRENTS", "IOPS_REMOVE"}; ///< Stats Labels + const std::vector SIZE_OP_S = {"WRITE_SIZE", + "READ_SIZE"}; ///< Stats Labels + + std::chrono::time_point + start; ///< When we started the server + + const unsigned int MAX_STATS = 1000000; ///< How many stats will be stored + + + std::map + IOPS; ///< Stores total value for global mean + std::map + SIZE; ///< Stores total value for global mean + + std::map>> + TIME_IOPS; ///< Stores timestamp when an operation comes removes if + ///< first operation if > 10 minutes Different means will + ///< be stored and cached 1 minuted + + + std::map, + unsigned long long>>> + TIME_SIZE; ///< For size operations we need to store the timestamp + ///< and the size + + + std::thread t_output; ///< Thread that outputs stats info + bool output_thread_; ///< Enables or disables the output thread + + bool running = + true; ///< Controls the destruction of the class/stops the thread + /** + * @brief Sends all the stats to the screen + * Debug Function + * + * @param d is the time between output + * @param file_output is the output file + */ + void + output(std::chrono::seconds d, std::string file_output); + + std::map, unsigned int> + CHUNK_READ; ///< Stores the number of times a chunk/file is read + std::map, unsigned int> + CHUNK_WRITE; ///< Stores the number of times a chunk/file is write + + /** + * @brief Called by output to generate CHUNK map + * + * @param output is the output stream + */ + void + output_map(std::ofstream& output); + + + /** + * @brief Dumps all the means from the stats + * @param of Output stream + */ + void + dump(std::ofstream& of); + +public: + /** + * @brief Starts the Stats module and initializes structures + * @param output_thread creates an aditional thread that outputs the stats + * @param filename file where to write the output + */ + Stats(bool output_thread, std::string filename); + + /** + * @brief Destroys the class, and any associated thread + * + */ + ~Stats(); + + /** + * @brief Adds a new read access to the chunk/path specified + * + * @param path + * @param chunk + */ + void + add_read(std::string path, unsigned long long chunk); + /** + * @brief Adds a new write access to the chunk/path specified + * + * @param path + * @param chunk + */ + void + add_write(std::string path, unsigned long long chunk); + + + /** + * Add a new value for a IOPS, that does not involve any size + * No value needed as they are simple (1 create, 1 read...) + * Size operations internally call this operation (read,write) + * + * @param IOPS_OP Which operation to add + */ + + void add_value_iops(enum IOPS_OP); + + /** + * @brief Store a new stat point, with a size value. + * If it involves a IO operations it will call the corresponding + * operation + * + * @param SIZE_OP Which operation we refer + * @param value to store (SIZE_OP) + */ + void + add_value_size(enum SIZE_OP, unsigned long long value); + + /** + * @brief Get the total mean value of the asked stat + * This can be provided inmediately without cost + * @return mean value + */ + double get_mean(enum IOPS_OP); + + + /** + * @brief Get the total mean value of the asked stat + * This can be provided inmediately without cost + * @return mean value + */ + double get_mean(enum SIZE_OP); + + /** + * @brief Get all the means (total, 1,5 and 10 minutes) for a SIZE_OP + * Returns precalculated values if we just calculated them 1 minute ago + * + * @return std::vector< double > with 4 means + */ + std::vector get_four_means(enum SIZE_OP); + + /** + * @brief Get all the means (total, 1,5 and 10 minutes) for a IOPS_OP + * Returns precalculated values if we just calculated them 1 minute ago + * + * @return std::vector< double > with 4 means + */ + std::vector get_four_means(enum IOPS_OP); +}; + +} // namespace gkfs::utils + +#endif // GKFS_COMMON_STATS_HPP \ No newline at end of file diff --git a/include/daemon/classes/fs_data.hpp b/include/daemon/classes/fs_data.hpp index 06e71468c693058661b79b65184d1eaaf2de969f..2427e5c4e7834ff7496709b1a298d0631e92ca46 100644 --- a/include/daemon/classes/fs_data.hpp +++ b/include/daemon/classes/fs_data.hpp @@ -46,6 +46,11 @@ namespace data { class ChunkStorage; } +/* Forward declarations */ +namespace utils { +class Stats; +} + namespace daemon { class FsData { @@ -85,6 +90,11 @@ private: bool link_cnt_state_; bool blocks_state_; + // Statistics + std::shared_ptr stats_; + bool output_stats_ = false; + std::string stats_file_; + public: static FsData* getInstance() { @@ -209,8 +219,30 @@ public: void parallax_size_md(unsigned int size_md); + + const std::shared_ptr& + stats() const; + + void + stats(const std::shared_ptr& stats); + + void + close_stats(); + + bool + output_stats() const; + + void + output_stats(bool output_stats); + + std::string + stats_file() const; + + void + stats_file(std::string stats_file); }; + } // namespace daemon } // namespace gkfs diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 74b80e58c2536c5465fd5494ed6b165b28d3f948..911e6f3fdd669880830eebf756e0acce5acd35a5 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -39,10 +39,23 @@ target_sources(distributor ${CMAKE_CURRENT_LIST_DIR}/rpc/distributor.cpp ) +add_library(statistics STATIC) +set_property(TARGET statistics PROPERTY POSITION_INDEPENDENT_CODE ON) +target_sources(statistics + PUBLIC + ${INCLUDE_DIR}/common/statistics/stats.hpp + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/statistics/stats.cpp + ) + if(GKFS_ENABLE_CODE_COVERAGE) target_code_coverage(distributor AUTO) endif() +if(GKFS_ENABLE_CODE_COVERAGE) + target_code_coverage(statistics AUTO) +endif() + # get spdlog set(FETCHCONTENT_QUIET ON) diff --git a/src/common/statistics/stats.cpp b/src/common/statistics/stats.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3b522e767e176d8c5d8009c4a4e24d835f89e34 --- /dev/null +++ b/src/common/statistics/stats.cpp @@ -0,0 +1,281 @@ +/* + Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2022, Johannes Gutenberg Universitaet Mainz, Germany + + This software was partially supported by the + EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). + + This software was partially supported by the + ADA-FS project under the SPPEXA project funded by the DFG. + + This file is part of GekkoFS. + + GekkoFS 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. + + GekkoFS 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 GNU General Public License + along with GekkoFS. If not, see . + + SPDX-License-Identifier: GPL-3.0-or-later +*/ + + +#include + +using namespace std; + +namespace gkfs::utils { + +Stats::Stats(bool output_thread, std::string stats_file) { + + // Init clocks + start = std::chrono::steady_clock::now(); + + // To simplify the control we add an element into the different maps + // Statistaclly will be negligible... and we get a faster flow + + for(auto e : all_IOPS_OP) { + IOPS[e] = 0; + TIME_IOPS[e].push_back(std::chrono::steady_clock::now()); + } + + for(auto e : all_SIZE_OP) { + SIZE[e] = 0; + TIME_SIZE[e].push_back(pair(std::chrono::steady_clock::now(), 0.0)); + } + + output_thread_ = output_thread; + + if(output_thread_) { + t_output = std::thread([this, stats_file] { + output(std::chrono::duration(10s), stats_file); + }); + } +} + +Stats::~Stats() { + // We do not need a mutex for that + if(output_thread_) { + running = false; + t_output.join(); + } +} + +void +Stats::add_read(std::string path, unsigned long long chunk) { + CHUNK_READ[pair(path, chunk)]++; +} + +void +Stats::add_write(std::string path, unsigned long long chunk) { + CHUNK_WRITE[pair(path, chunk)]++; +} + + +void +Stats::output_map(std::ofstream& output) { + // Ordering + map>> + ORDER_WRITE; + + map>> + ORDER_READ; + + for(auto i : CHUNK_READ) { + ORDER_READ[i.second].insert(i.first); + } + + for(auto i : CHUNK_WRITE) { + ORDER_WRITE[i.second].insert(i.first); + } + + auto CHUNK_MAP = + [](std::string caption, + map>>& ORDER, + std::ofstream& output) { + output << caption << std::endl; + for(auto k : ORDER) { + output << k.first << " -- "; + for(auto v : k.second) { + output << v.first << " // " << v.second << endl; + } + } + }; + + CHUNK_MAP("READ CHUNK MAP", ORDER_READ, output); + CHUNK_MAP("WRITE CHUNK MAP", ORDER_WRITE, output); +} + +void +Stats::add_value_iops(enum IOPS_OP iop) { + IOPS[iop]++; + auto now = std::chrono::steady_clock::now(); + + + if((now - TIME_IOPS[iop].front()) > std::chrono::duration(10s)) { + TIME_IOPS[iop].pop_front(); + } else if(TIME_IOPS[iop].size() >= MAX_STATS) + TIME_IOPS[iop].pop_front(); + + TIME_IOPS[iop].push_back(std::chrono::steady_clock::now()); +} + +void +Stats::add_value_size(enum SIZE_OP iop, unsigned long long value) { + auto now = std::chrono::steady_clock::now(); + SIZE[iop] += value; + if((now - TIME_SIZE[iop].front().first) > std::chrono::duration(10s)) { + TIME_SIZE[iop].pop_front(); + } else if(TIME_SIZE[iop].size() >= MAX_STATS) + TIME_SIZE[iop].pop_front(); + + TIME_SIZE[iop].push_back(pair(std::chrono::steady_clock::now(), value)); + + if(iop == SIZE_OP::READ_SIZE) + IOPS[IOPS_OP::IOPS_READ]++; + else if(iop == SIZE_OP::WRITE_SIZE) + IOPS[IOPS_OP::IOPS_WRITE]++; +} + +/** + * @brief Get the total mean value of the asked stat + * This can be provided inmediately without cost + * @return mean value + */ +double +Stats::get_mean(enum SIZE_OP sop) { + auto now = std::chrono::steady_clock::now(); + auto duration = + std::chrono::duration_cast(now - start); + double value = (double) SIZE[sop] / (double) duration.count(); + return value; +} + +double +Stats::get_mean(enum IOPS_OP iop) { + auto now = std::chrono::steady_clock::now(); + auto duration = + std::chrono::duration_cast(now - start); + double value = (double) IOPS[iop] / (double) duration.count(); + return value; +} + + +/** + * @brief Get all the means (total, 1,5 and 10 minutes) for a SIZE_OP + * Returns precalculated values if we just calculated them 1 minute ago + * // TODO: cache + * @return std::vector< double > with 4 means + */ +std::vector +Stats::get_four_means(enum SIZE_OP sop) { + std::vector results = {0, 0, 0, 0}; + auto now = std::chrono::steady_clock::now(); + for(auto e : TIME_SIZE[sop]) { + auto duration = + std::chrono::duration_cast(now - e.first) + .count(); + if(duration > 10) + break; + + results[3] += e.second; + if(duration > 5) + continue; + results[2] += e.second; + if(duration > 1) + continue; + results[1] += e.second; + } + // Mean in MB/s + results[0] = get_mean(sop) / (1024.0 * 1024.0); + results[3] /= 10 * 60 * (1024.0 * 1024.0); + results[2] /= 5 * 60 * (1024.0 * 1024.0); + results[1] /= 60 * (1024.0 * 1024.0); + + return results; +} + + +std::vector +Stats::get_four_means(enum IOPS_OP iop) { + std::vector results = {0, 0, 0, 0}; + auto now = std::chrono::steady_clock::now(); + for(auto e : TIME_IOPS[iop]) { + auto duration = + std::chrono::duration_cast(now - e) + .count(); + if(duration > 10) + break; + + results[3]++; + if(duration > 5) + continue; + results[2]++; + if(duration > 1) + continue; + results[1]++; + } + + results[0] = get_mean(iop); + results[3] /= 10 * 60; + results[2] /= 5 * 60; + results[1] /= 60; + + return results; +} + +void +Stats::dump(std::ofstream& of) { + for(auto e : all_IOPS_OP) { + auto tmp = get_four_means(e); + + of << "Stats " << IOPS_OP_S[static_cast(e)] + << " IOPS/s (avg, 1 min, 5 min, 10 min) \t\t"; + for(auto mean : tmp) { + of << std::setprecision(4) << std::setw(9) << mean << " - "; + } + of << std::endl; + } + for(auto e : all_SIZE_OP) { + auto tmp = get_four_means(e); + + of << "Stats " << SIZE_OP_S[static_cast(e)] + << " MB/s (avg, 1 min, 5 min, 10 min) \t\t"; + for(auto mean : tmp) { + of << std::setprecision(4) << std::setw(9) << mean << " - "; + } + of << std::endl; + } + of << std::endl; +} +void +Stats::output(std::chrono::seconds d, std::string file_output) { + int times = 0; + std::ofstream of(file_output, std::ios_base::openmode::_S_trunc); + + while(running) { + dump(of); + std::chrono::seconds a = 0s; + + times++; +#ifdef GKFS_CHUNK_STATS + if(times % 4 == 0) + output_map(of); +#endif + + while(running and a < d) { + a += 1s; + std::this_thread::sleep_for(1s); + } + } +} + +} // namespace gkfs::utils diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index e3703d73b50b0d824c219b47b78a9b85ada4d296..f91415eee9bbdf02e61ae5a03b6e9de723277057 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -63,6 +63,7 @@ set(DAEMON_LINK_LIBRARIES metadata_db storage distributor + statistics log_util env_util spdlog diff --git a/src/daemon/classes/fs_data.cpp b/src/daemon/classes/fs_data.cpp index 1611e9e8a3bf94fabda6dc0386b31bf8c4a5aece..cea2a22049d3412e9c3e964fd34d47b2e7613c9c 100644 --- a/src/daemon/classes/fs_data.cpp +++ b/src/daemon/classes/fs_data.cpp @@ -221,4 +221,40 @@ FsData::parallax_size_md(unsigned int size_md) { size_md * 1024ull * 1024ull * 1024ull); } +const std::shared_ptr& +FsData::stats() const { + return stats_; +} + +void +FsData::stats(const std::shared_ptr& stats) { + FsData::stats_ = stats; +} + +void +FsData::close_stats() { + stats_.reset(); +} + + +bool +FsData::output_stats() const { + return output_stats_; +} + +void +FsData::output_stats(bool output_stats) { + FsData::output_stats_ = output_stats; +} + +std::string +FsData::stats_file() const { + return stats_file_; +} + +void +FsData::stats_file(std::string stats_file) { + FsData::stats_file_ = stats_file; +} + } // namespace gkfs::daemon diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 1ed24e9a22af443cc1a1ce16630f592c5fd56d6a..f6bb69b2a53ab953ed547afb6eb4874471ea68b6 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -52,6 +52,7 @@ #include #endif +#include #include #include @@ -81,6 +82,7 @@ struct cli_options { string rpc_protocol; string dbbackend; string parallax_size; + string stats_file; }; /** @@ -291,6 +293,10 @@ init_environment() { } #endif + // Initialize Stats + GKFS_DATA->stats(std::make_shared( + GKFS_DATA->output_stats(), GKFS_DATA->stats_file())); + // Initialize data backend auto chunk_storage_path = fmt::format("{}/{}", GKFS_DATA->rootdir(), gkfs::config::data::chunk_dir); @@ -420,6 +426,7 @@ destroy_enviroment() { fs::remove_all(GKFS_DATA->metadir(), ecode); fs::remove_all(GKFS_DATA->rootdir(), ecode); } + GKFS_DATA->close_stats(); } /** @@ -644,6 +651,13 @@ parse_input(const cli_options& opts, const CLI::App& desc) { if(desc.count("--parallaxsize")) { // Size in GB GKFS_DATA->parallax_size_md(stoi(opts.parallax_size)); } + if(desc.count("--output-stats")) { + auto stats_file = opts.stats_file; + GKFS_DATA->stats_file(stats_file); + GKFS_DATA->output_stats(true); + GKFS_DATA->spdlogger()->debug("{}() Stats Enabled: '{}'", __func__, + stats_file); + } } /** @@ -711,6 +725,10 @@ main(int argc, const char* argv[]) { desc.add_option("--parallaxsize", opts.parallax_size, "parallaxdb - metadata file size in GB (default 8GB), " "used only with new files"); + desc.add_option( + "--output-stats", opts.stats_file, + "Creates a thread that outputs the server stats each 10s, to the file specified"); + desc.add_flag("--version", "Print version and exit."); // clang-format on try { diff --git a/src/daemon/handler/srv_data.cpp b/src/daemon/handler/srv_data.cpp index d7b9a6acef8a995cd9bfa6fc952cef6361e2b1ca..59b62859a5c615fa3680871e1c235e35bab59af8 100644 --- a/src/daemon/handler/srv_data.cpp +++ b/src/daemon/handler/srv_data.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #ifdef GKFS_ENABLE_AGIOS #include @@ -113,6 +114,10 @@ rpc_srv_write(hg_handle_t handle) { "{}() path: '{}' chunk_start '{}' chunk_end '{}' chunk_n '{}' total_chunk_size '{}' bulk_size: '{}' offset: '{}'", __func__, in.path, in.chunk_start, in.chunk_end, in.chunk_n, in.total_chunk_size, bulk_size, in.offset); + + GKFS_DATA->stats()->add_value_size(gkfs::utils::Stats::SIZE_OP::WRITE_SIZE, + bulk_size); + #ifdef GKFS_ENABLE_AGIOS int* data; ABT_eventual eventual = ABT_EVENTUAL_NULL; @@ -233,6 +238,9 @@ rpc_srv_write(hg_handle_t handle) { __func__, chnk_id_file, host_id, chnk_id_curr); continue; } +#ifdef GKFS_CHUNK_STATS + GKFS_DATA->stats()->add_write(in.path, chnk_id_file); +#endif #endif chnk_ids_host[chnk_id_curr] = @@ -404,6 +412,10 @@ rpc_srv_read(hg_handle_t handle) { "{}() path: '{}' chunk_start '{}' chunk_end '{}' chunk_n '{}' total_chunk_size '{}' bulk_size: '{}' offset: '{}'", __func__, in.path, in.chunk_start, in.chunk_end, in.chunk_n, in.total_chunk_size, bulk_size, in.offset); + + GKFS_DATA->stats()->add_value_size(gkfs::utils::Stats::SIZE_OP::READ_SIZE, + bulk_size); + #ifdef GKFS_ENABLE_AGIOS int* data; ABT_eventual eventual = ABT_EVENTUAL_NULL; @@ -513,6 +525,7 @@ rpc_srv_read(hg_handle_t handle) { __func__, chnk_id_file, host_id, chnk_id_curr); continue; } + GKFS_DATA->stats()->add_read(in.path, chnk_id_file); #endif chnk_ids_host[chnk_id_curr] = diff --git a/src/daemon/handler/srv_metadata.cpp b/src/daemon/handler/srv_metadata.cpp index 3cea7e3eaee40c939658037152fc6b8336b6c7d6..38f9035aa4fb9f12d259bf1649275112df7fa963 100644 --- a/src/daemon/handler/srv_metadata.cpp +++ b/src/daemon/handler/srv_metadata.cpp @@ -41,6 +41,7 @@ #include #include +#include using namespace std; @@ -77,6 +78,8 @@ rpc_srv_create(hg_handle_t handle) { // create metadentry gkfs::metadata::create(in.path, md); out.err = 0; + GKFS_DATA->stats()->add_value_iops( + gkfs::utils::Stats::IOPS_OP::IOPS_CREATE); } catch(const gkfs::metadata::ExistsException& e) { out.err = EEXIST; } catch(const std::exception& e) { @@ -84,6 +87,7 @@ rpc_srv_create(hg_handle_t handle) { __func__, e.what()); out.err = -1; } + GKFS_DATA->spdlogger()->debug("{}() Sending output err '{}'", __func__, out.err); auto hret = margo_respond(handle, &out); @@ -122,6 +126,8 @@ rpc_srv_stat(hg_handle_t handle) { GKFS_DATA->spdlogger()->debug("{}() path: '{}'", __func__, in.path); std::string val; + GKFS_DATA->stats()->add_value_iops(gkfs::utils::Stats::IOPS_OP::IOPS_STATS); + try { // get the metadata val = gkfs::metadata::get_str(in.path); @@ -241,6 +247,8 @@ rpc_srv_remove_metadata(hg_handle_t handle) { if(S_ISREG(md.mode()) && (md.size() != 0)) GKFS_DATA->storage()->destroy_chunk_space(in.path); } + GKFS_DATA->stats()->add_value_iops( + gkfs::utils::Stats::IOPS_OP::IOPS_REMOVE); } catch(const gkfs::metadata::DBException& e) { GKFS_DATA->spdlogger()->error("{}(): path '{}' message '{}'", __func__, in.path, e.what()); @@ -535,6 +543,8 @@ rpc_srv_get_dirents(hg_handle_t handle) { vector> entries{}; try { entries = gkfs::metadata::get_dirents(in.path); + GKFS_DATA->stats()->add_value_iops( + gkfs::utils::Stats::IOPS_OP::IOPS_DIRENTS); } catch(const ::exception& e) { GKFS_DATA->spdlogger()->error("{}() Error during get_dirents(): '{}'", __func__, e.what()); diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 6f5624daec3a17f0ca0d24ef9367f7ca63f4ce7d..2b770ae84c7c90e09ed664593fe00ae1fa2ea516 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -251,7 +251,8 @@ class Daemon: '--rootdir', self.rootdir, '-l', self._address, '--metadir', self._metadir, - '--dbbackend', self._database] + '--dbbackend', self._database, + '--output-stats', self.logdir / 'stats.log' ] if self._database == "parallaxdb" : args.append('--clean-rootdir-finish')