diff --git a/CMakeLists.txt b/CMakeLists.txt index 12a74627..5a9041bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,33 +44,33 @@ project(${PROJECT} CXX) ### # compilation options ### -if(WIN32) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /O2 /bigobj") +if (WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /O2 /bigobj") - # was causing conflics with gtest build - string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + # was causing conflics with gtest build + string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - if("${MSVC_RUNTIME_LIBRARY_CONFIG}" STREQUAL "") - set(MSVC_RUNTIME_LIBRARY_CONFIG "/MT") - endif() + if ("${MSVC_RUNTIME_LIBRARY_CONFIG}" STREQUAL "") + set(MSVC_RUNTIME_LIBRARY_CONFIG "/MT") + endif () - foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE) - if("${MSVC_RUNTIME_LIBRARY_CONFIG}" STREQUAL "/MT") - string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") - elseif("${MSVC_RUNTIME_LIBRARY_CONFIG}" STREQUAL "/MD") - string(REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}") - else() - string(REPLACE "/MD" "${MSVC_RUNTIME_LIBRARY_CONFIG}" ${flag_var} "${${flag_var}}") - string(REPLACE "/MT" "${MSVC_RUNTIME_LIBRARY_CONFIG}" ${flag_var} "${${flag_var}}") - endif() - endforeach() + foreach (flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE) + if ("${MSVC_RUNTIME_LIBRARY_CONFIG}" STREQUAL "/MT") + string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + elseif ("${MSVC_RUNTIME_LIBRARY_CONFIG}" STREQUAL "/MD") + string(REPLACE "/MT" "/MD" ${flag_var} "${${flag_var}}") + else () + string(REPLACE "/MD" "${MSVC_RUNTIME_LIBRARY_CONFIG}" ${flag_var} "${${flag_var}}") + string(REPLACE "/MT" "${MSVC_RUNTIME_LIBRARY_CONFIG}" ${flag_var} "${${flag_var}}") + endif () + endforeach () - add_definitions(-D_UNICODE) - add_definitions(-DUNICODE) - add_definitions(-DWIN32_LEAN_AND_MEAN) -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -O3") -endif(WIN32) + add_definitions(-D_UNICODE) + add_definitions(-DUNICODE) + add_definitions(-DWIN32_LEAN_AND_MEAN) +else () + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -O3") +endif (WIN32) ### @@ -81,9 +81,9 @@ find_library(TACOPIE_LIBRARY tacopie) set(CPP_REDIS_INCLUDES ${PROJECT_SOURCE_DIR}/includes) set(DEPS_INCLUDES ${PROJECT_SOURCE_DIR}/deps/include) -if(NOT USE_CUSTOM_TCP_CLIENT) - set(DEPS_INCLUDES ${DEPS_INCLUDES} ${TACOPIE_INCLUDE_DIR}) -endif() +if (NOT USE_CUSTOM_TCP_CLIENT) + set(DEPS_INCLUDES ${DEPS_INCLUDES} ${TACOPIE_INCLUDE_DIR}) +endif () set(DEPS_LIBRARIES ${PROJECT_SOURCE_DIR}/deps/lib) @@ -98,31 +98,31 @@ include_directories(${CPP_REDIS_INCLUDES} ${DEPS_INCLUDES}) # sources ### set(SRC_DIRS "sources" - "sources/builders" - "sources/core" - "sources/misc" - "sources/network" - "includes/cpp_redis" - "includes/cpp_redis/builders" - "includes/cpp_redis/core" - "includes/cpp_redis/misc" - "includes/cpp_redis/network") - -foreach(dir ${SRC_DIRS}) - # get directory sources and headers - file(GLOB s_${dir} "${dir}/*.cpp") - file(GLOB h_${dir} "${dir}/*.hpp") - file(GLOB i_${dir} "${dir}/*.ipp") - - # set sources - set(SOURCES ${SOURCES} ${s_${dir}} ${h_${dir}} ${i_${dir}}) -endforeach() + "sources/builders" + "sources/core" + "sources/misc" + "sources/network" + "includes/cpp_redis" + "includes/cpp_redis/builders" + "includes/cpp_redis/core" + "includes/cpp_redis/misc" + "includes/cpp_redis/network") + +foreach (dir ${SRC_DIRS}) + # get directory sources and headers + file(GLOB s_${dir} "${dir}/*.cpp") + file(GLOB h_${dir} "${dir}/*.hpp") + file(GLOB i_${dir} "${dir}/*.ipp") + + # set sources + set(SOURCES ${SOURCES} ${s_${dir}} ${h_${dir}} ${i_${dir}}) +endforeach () # filter tcp_client if no tacopie -if(USE_CUSTOM_TCP_CLIENT) - file(GLOB tacopie_cpp "sources/network/tcp_client.cpp") - file(GLOB tacopie_h "includes/cpp_redis/network/tcp_client.hpp") - list(REMOVE_ITEM SOURCES ${tacopie_cpp} ${tacopie_h}) -endif(USE_CUSTOM_TCP_CLIENT) +if (USE_CUSTOM_TCP_CLIENT) + file(GLOB tacopie_cpp "sources/network/tcp_client.cpp") + file(GLOB tacopie_h "includes/cpp_redis/network/tcp_client.hpp") + list(REMOVE_ITEM SOURCES ${tacopie_cpp} ${tacopie_h}) +endif (USE_CUSTOM_TCP_CLIENT) ### @@ -144,39 +144,39 @@ configure_file("cpp_redis.pc.in" "${CMAKE_PKGCONFIG_OUTPUT_DIRECTORY}/cpp_redis. add_library(${PROJECT} ${SOURCES}) set_property(TARGET ${PROJECT} PROPERTY POSITION_INDEPENDENT_CODE ON) -if(WIN32) - set_target_properties(${PROJECT} - PROPERTIES COMPILE_PDB_NAME ${PROJECT} - COMPILE_PDB_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) -endif(WIN32) +if (WIN32) + set_target_properties(${PROJECT} + PROPERTIES COMPILE_PDB_NAME ${PROJECT} + COMPILE_PDB_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) +endif (WIN32) -if(WIN32) - target_link_libraries(${PROJECT} ws2_32) -else() - target_link_libraries(${PROJECT} pthread) -endif(WIN32) +if (WIN32) + target_link_libraries(${PROJECT} ws2_32) +else () + target_link_libraries(${PROJECT} pthread) +endif (WIN32) -if(TACOPIE_LIBRARY) - target_link_libraries(${PROJECT} ${TACOPIE_LIBRARY}) -else() - target_link_libraries(${PROJECT} tacopie) -endif(TACOPIE_LIBRARY) +if (TACOPIE_LIBRARY) + target_link_libraries(${PROJECT} ${TACOPIE_LIBRARY}) +else () + target_link_libraries(${PROJECT} tacopie) +endif (TACOPIE_LIBRARY) # __CPP_REDIS_READ_SIZE -if(READ_SIZE) - set_property(TARGET ${PROJECT} APPEND_STRING PROPERTY COMPILE_DEFINITIONS " __CPP_REDIS_READ_SIZE=${READ_SIZE}") -endif(READ_SIZE) +if (READ_SIZE) + set_property(TARGET ${PROJECT} APPEND_STRING PROPERTY COMPILE_DEFINITIONS " __CPP_REDIS_READ_SIZE=${READ_SIZE}") +endif (READ_SIZE) # __CPP_REDIS_LOGGING_ENABLED -if(LOGGING_ENABLED) -set_property(TARGET ${PROJECT} APPEND_STRING PROPERTY COMPILE_DEFINITIONS " __CPP_REDIS_LOGGING_ENABLED=${LOGGING_ENABLED}") -endif(LOGGING_ENABLED) +if (LOGGING_ENABLED) + set_property(TARGET ${PROJECT} APPEND_STRING PROPERTY COMPILE_DEFINITIONS " __CPP_REDIS_LOGGING_ENABLED=${LOGGING_ENABLED}") +endif (LOGGING_ENABLED) # __CPP_REDIS_USE_CUSTOM_TCP_CLIENT -if(USE_CUSTOM_TCP_CLIENT) - set_property(TARGET ${PROJECT} APPEND_STRING PROPERTY COMPILE_DEFINITIONS " __CPP_REDIS_USE_CUSTOM_TCP_CLIENT=${USE_CUSTOM_TCP_CLIENT}") -endif(USE_CUSTOM_TCP_CLIENT) +if (USE_CUSTOM_TCP_CLIENT) + set_property(TARGET ${PROJECT} APPEND_STRING PROPERTY COMPILE_DEFINITIONS " __CPP_REDIS_USE_CUSTOM_TCP_CLIENT=${USE_CUSTOM_TCP_CLIENT}") +endif (USE_CUSTOM_TCP_CLIENT) ### @@ -194,30 +194,30 @@ install(DIRECTORY ${CPP_REDIS_INCLUDES}/ DESTINATION include USE_SOURCE_PERMISSI ### # examples ### -if(BUILD_EXAMPLES) - add_subdirectory(examples) - # Reset variable to false to ensure tacopie does no build examples - set(BUILD_EXAMPLES false) -endif(BUILD_EXAMPLES) +if (BUILD_EXAMPLES) + add_subdirectory(examples) + # Reset variable to false to ensure tacopie does no build examples + set(BUILD_EXAMPLES false) +endif (BUILD_EXAMPLES) ### # tests ### -if(BUILD_TESTS) - enable_testing() - add_subdirectory(tests) - ExternalProject_Add("googletest" - GIT_REPOSITORY "https://github.com/google/googletest.git" - CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${PROJECT_SOURCE_DIR}/deps") - # Reset variable to false to ensure tacopie does no build tests - set(BUILD_TESTS false) -endif(BUILD_TESTS) +if (BUILD_TESTS) + enable_testing() + add_subdirectory(tests) + ExternalProject_Add("googletest" + GIT_REPOSITORY "https://github.com/google/googletest.git" + CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${PROJECT_SOURCE_DIR}/deps") + # Reset variable to false to ensure tacopie does no build tests + set(BUILD_TESTS false) +endif (BUILD_TESTS) ### # tacopie ### -if(NOT TACOPIE_LIBRARY AND NOT USE_CUSTOM_TCP_CLIENT) - set(SOURCES) # reset the SOURCES var so that the tacopie project won't include the cpp_redis sources too - add_subdirectory(tacopie) -endif(NOT TACOPIE_LIBRARY AND NOT USE_CUSTOM_TCP_CLIENT) +if (NOT TACOPIE_LIBRARY AND NOT USE_CUSTOM_TCP_CLIENT) + set(SOURCES) # reset the SOURCES var so that the tacopie project won't include the cpp_redis sources too + add_subdirectory(tacopie) +endif (NOT TACOPIE_LIBRARY AND NOT USE_CUSTOM_TCP_CLIENT) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 550128d8..b5502878 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -39,50 +39,29 @@ include_directories(${CPP_REDIS_INCLUDES}) ### link_directories(${DEPS_LIBRARIES}) - -### -# executable -### -add_executable(cpp_redis_client cpp_redis_client.cpp) -target_link_libraries(cpp_redis_client cpp_redis) - -add_executable(cpp_redis_consumer cpp_redis_consumer.cpp) -target_link_libraries(cpp_redis_consumer cpp_redis) - -add_executable(cpp_redis_future_client cpp_redis_future_client.cpp) -target_link_libraries(cpp_redis_future_client cpp_redis) - -add_executable(cpp_redis_subscriber cpp_redis_subscriber.cpp) -target_link_libraries(cpp_redis_subscriber cpp_redis) - -add_executable(cpp_redis_logger cpp_redis_logger.cpp) -target_link_libraries(cpp_redis_logger cpp_redis) - -add_executable(cpp_redis_kill cpp_redis_kill.cpp) -target_link_libraries(cpp_redis_kill cpp_redis) - -add_executable(cpp_redis_streams_client cpp_redis_streams_client.cpp) -target_link_libraries(cpp_redis_streams_client cpp_redis) - -add_executable(cpp_redis_high_availability_client cpp_redis_high_availability_client.cpp) -target_link_libraries(cpp_redis_high_availability_client cpp_redis) - - -### -# link libs -### -if(WIN32) - target_link_libraries(cpp_redis_client ws2_32) - target_link_libraries(cpp_redis_future_client ws2_32) - target_link_libraries(cpp_redis_subscriber ws2_32) - target_link_libraries(cpp_redis_logger ws2_32) - target_link_libraries(cpp_redis_kill ws2_32) - target_link_libraries(cpp_redis_high_availability_client ws2_32) -else() - target_link_libraries(cpp_redis_client pthread) - target_link_libraries(cpp_redis_future_client pthread) - target_link_libraries(cpp_redis_subscriber pthread) - target_link_libraries(cpp_redis_logger pthread) - target_link_libraries(cpp_redis_kill pthread) - target_link_libraries(cpp_redis_high_availability_client pthread) -endif(WIN32) +set(EXAMPLES cpp_redis_client + cpp_redis_consumer + cpp_redis_future_client + cpp_redis_subscriber + cpp_redis_logger + cpp_redis_kill + cpp_redis_streams_client + cpp_redis_high_availability_client + ) + +foreach(EXAMPLE IN ITEMS ${EXAMPLES}) + ### + # executable + ### + add_executable(${EXAMPLE} ${EXAMPLE}.cpp) + target_link_libraries(${EXAMPLE} cpp_redis) + + ### + # link libs + ### + if(WIN32) + target_link_libraries(${EXAMPLE} ws2_32) + else() + target_link_libraries(${EXAMPLE} pthread) + endif(WIN32) +endforeach(EXAMPLE) \ No newline at end of file diff --git a/examples/cpp_redis_client.cpp b/examples/cpp_redis_client.cpp index b6778bb9..1d3324db 100644 --- a/examples/cpp_redis_client.cpp +++ b/examples/cpp_redis_client.cpp @@ -54,7 +54,7 @@ main(void) { } }); - auto replcmd = [](cpp_redis::reply &reply) { + auto replcmd = [](const cpp_redis::reply &reply) { std::cout << "set hello 42: " << reply << std::endl; // if (reply.is_string()) // do_something_with_string(reply.as_string()) diff --git a/examples/cpp_redis_consumer.cpp b/examples/cpp_redis_consumer.cpp index 3cb31233..b6da63d4 100644 --- a/examples/cpp_redis_consumer.cpp +++ b/examples/cpp_redis_consumer.cpp @@ -39,7 +39,7 @@ sigint_handler(int) { } int -main(void) { +main() { #ifdef _WIN32 //! Windows netword DLL init WORD version = MAKEWORD(2, 2); @@ -53,7 +53,8 @@ main(void) { //! Enable logging - const std::string group_name = "groupone"; + //const std::string group_name = "groupone"; + const std::vector group_names = {"groupone"}; //, "grouptwo"}; const std::string session_name = "sessone"; const std::string consumer_name = "ABCD"; @@ -68,11 +69,36 @@ main(void) { } }); - sub.subscribe(group_name, [](const cpp_redis::message_type msg){ - std::cout << "Id in the cb: " << msg.get_id() << std::endl; + sub.auth("{redis_key}"); + + for (auto &group : group_names) { + + sub.subscribe(group, + [group](const cpp_redis::message_type msg) { + cpp_redis::consumer_response_t res; + // Callback will run for each message obtained from the queue + std::cout << "Group: " << group << std::endl; + std::cout << "Id in the cb: " << msg.get_id() << std::endl; + res.insert({"Id", msg.get_id()}); + return res; + }, + [group](int ack_status) { + // Callback will run upon return of xack + std::cout << "Group: " << group << std::endl; + std::cout << "Ack status: " << ack_status << std::endl; + }); + } - return msg; - }); + /*sub.subscribe(group_name, + [](const cpp_redis::message_type msg) { + // Callback will run for each message obtained from the queue + std::cout << "Id in the cb: " << msg.get_id() << std::endl; + return msg; + }, + [](int ack_status) { + // Callback will run upon return of xack + std::cout << "Ack status: " << ack_status << std::endl; + });*/ sub.commit(); diff --git a/examples/cpp_redis_streams_client.cpp b/examples/cpp_redis_streams_client.cpp index 6c9868c4..eefb7c24 100644 --- a/examples/cpp_redis_streams_client.cpp +++ b/examples/cpp_redis_streams_client.cpp @@ -27,7 +27,7 @@ #endif /* _WIN32 */ int -main(void) { +main() { #ifdef _WIN32 //! Windows netword DLL init WORD version = MAKEWORD(2, 2); diff --git a/includes/cpp_redis/builders/array_builder.hpp b/includes/cpp_redis/builders/array_builder.hpp index 948b067c..e788c4ac 100644 --- a/includes/cpp_redis/builders/array_builder.hpp +++ b/includes/cpp_redis/builders/array_builder.hpp @@ -20,7 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#pragma once +#ifndef CPP_REDIS_BUILDERS_ARRAY_BUILDER_HPP_ +#define CPP_REDIS_BUILDERS_ARRAY_BUILDER_HPP_ #include #include @@ -28,89 +29,116 @@ namespace cpp_redis { -namespace builders { - -//! -//! builder to build redis array replies -//! -class array_builder : public builder_iface { -public: - //! ctor - array_builder(void); - //! dtor - ~array_builder(void) = default; - - //! copy ctor - array_builder(const array_builder&) = delete; - //! assignment operator - array_builder& operator=(const array_builder&) = delete; - -public: - //! - //! take data as parameter which is consumed to build the reply - //! every bytes used to build the reply must be removed from the buffer passed as parameter - //! - //! \param data data to be consumed - //! \return current instance - //! - builder_iface& operator<<(std::string& data); - - //! - //! \return whether the reply could be built - //! - bool reply_ready(void) const; - - //! - //! \return reply object - //! - reply get_reply(void) const; - -private: - //! - //! take data as parameter which is consumed to determine array size - //! every bytes used to build size is removed from the buffer passed as parameter - //! - //! \param buffer data to be consumer - //! \return true if the size could be found - //! - bool fetch_array_size(std::string& buffer); - - //! - //! take data as parameter which is consumed to build an array row - //! every bytes used to build row is removed from the buffer passed as parameter - //! - //! \param buffer data to be consumer - //! \return true if the row could be built - //! - bool build_row(std::string& buffer); - -private: - //! - //! builder used to fetch the array size - //! - integer_builder m_int_builder; - - //! - //! built array size - //! - uint64_t m_array_size; - - //! - //! current builder used to build current row - //! - std::unique_ptr m_current_builder; - - //! - //! whether the reply is ready or not - //! - bool m_reply_ready; - - //! - //! reply to be built (or built) - //! - reply m_reply; -}; - -} // namespace builders + namespace builders { + +/** + * builder to build redis array replies + * + */ + class array_builder : public builder_iface { + public: +/** + * ctor + * + */ + array_builder(); + +/** + * dtor + * + */ + ~array_builder() override = default; + +/** + * copy ctor + * + */ + array_builder(const array_builder &) = delete; + +/** + * assignment operator + * + */ + array_builder &operator=(const array_builder &) = delete; + + public: +/** + * take data as parameter which is consumed to build the reply + * every bytes used to build the reply must be removed from the buffer passed as parameter + * + * @param data data to be consumed + * @return current instance + * + */ + builder_iface &operator<<(std::string &data) override; + +/** + * @return whether the reply could be built + * + */ + bool reply_ready() const override; + +/** + * @return reply object + * + */ + reply get_reply() const override; + + private: +/** + * take data as parameter which is consumed to determine array size + * every bytes used to build size is removed from the buffer passed as parameter + * + * @param buffer data to be consumer + * @return true if the size could be found + * + */ + bool fetch_array_size(std::string &buffer); + +/** + * take data as parameter which is consumed to build an array row + * every bytes used to build row is removed from the buffer passed as parameter + * + * @param buffer data to be consumer + * @return true if the row could be built + * + */ + bool build_row(std::string &buffer); + + private: +/** + * builder used to fetch the array size + * + */ + integer_builder m_int_builder; + +/** + * built array size + * + */ + uint64_t m_array_size; + +/** + * current builder used to build current row + * + */ + std::unique_ptr m_current_builder; + +/** + * whether the reply is ready or not + * + */ + bool m_reply_ready; + +/** + * reply to be built (or built) + * + */ + reply m_reply; + }; + + } // namespace builders } // namespace cpp_redis + +#endif diff --git a/includes/cpp_redis/builders/builder_iface.hpp b/includes/cpp_redis/builders/builder_iface.hpp index 68980cb4..8ac5904e 100644 --- a/includes/cpp_redis/builders/builder_iface.hpp +++ b/includes/cpp_redis/builders/builder_iface.hpp @@ -20,7 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#pragma once +#ifndef CPP_REDIS_BUILDERS_BUILDER_IFACE_HPP_ +#define CPP_REDIS_BUILDERS_BUILDER_IFACE_HPP_ #include #include @@ -29,35 +30,40 @@ namespace cpp_redis { -namespace builders { - -//! -//! interface inherited by all builders -//! -class builder_iface { -public: - virtual ~builder_iface() = default; - - //! - //! take data as parameter which is consumed to build the reply - //! every bytes used to build the reply must be removed from the buffer passed as parameter - //! - //! \param data data to be consumed - //! \return current instance - //! - virtual builder_iface& operator<<(std::string& data) = 0; - - //! - //! \return whether the reply could be built - //! - virtual bool reply_ready() const = 0; - - //! - //! \return reply object - //! - virtual reply get_reply() const = 0; -}; - -} // namespace builders + namespace builders { + + /** + * @brief interface inherited by all builders + */ + class builder_iface { + public: + virtual ~builder_iface() = default; + + /** + * take data as parameter which is consumed to build the reply + * every bytes used to build the reply must be removed from the buffer passed as parameter + * + * @param data data to be consumed + * @return current instance + * + */ + virtual builder_iface &operator<<(std::string &data) = 0; + + /** + * @return whether the reply could be built + * + */ + virtual bool reply_ready() const = 0; + + /** + * @return reply object + * + */ + virtual reply get_reply() const = 0; + }; + + } // namespace builders } // namespace cpp_redis + +#endif \ No newline at end of file diff --git a/includes/cpp_redis/builders/builders_factory.hpp b/includes/cpp_redis/builders/builders_factory.hpp index 97ec556b..9a3857d8 100644 --- a/includes/cpp_redis/builders/builders_factory.hpp +++ b/includes/cpp_redis/builders/builders_factory.hpp @@ -28,21 +28,21 @@ namespace cpp_redis { -namespace builders { - -//! -//! create a builder corresponding to the given id -//! * + for simple strings -//! * - for errors -//! * : for integers -//! * $ for bulk strings -//! * * for arrays -//! -//! \param id char that determines which builder to return -//! \return new builder instance depending on id value -//! -std::unique_ptr create_builder(char id); - -} // namespace builders + namespace builders { + + /** + * create a builder corresponding to the given id + * * + for simple strings + * * - for errors + * * : for integers + * * $ for bulk strings + * * * for arrays + * + * @param id char that determines which builder to return + * @return new builder instance depending on id value + */ + std::unique_ptr create_builder(char id); + + } // namespace builders } // namespace cpp_redis diff --git a/includes/cpp_redis/builders/bulk_string_builder.hpp b/includes/cpp_redis/builders/bulk_string_builder.hpp index 6abe53fd..7ea726b8 100644 --- a/includes/cpp_redis/builders/bulk_string_builder.hpp +++ b/includes/cpp_redis/builders/bulk_string_builder.hpp @@ -35,80 +35,80 @@ namespace builders { //! class bulk_string_builder : public builder_iface { public: - //! ctor - bulk_string_builder(void); - //! dtor - ~bulk_string_builder(void) = default; +//! ctor + bulk_string_builder(); +//! dtor + ~bulk_string_builder() override = default; - //! copy ctor +//! copy ctor bulk_string_builder(const bulk_string_builder&) = delete; - //! assignment operator +//! assignment operator bulk_string_builder& operator=(const bulk_string_builder&) = delete; public: - //! - //! take data as parameter which is consumed to build the reply - //! every bytes used to build the reply must be removed from the buffer passed as parameter - //! - //! \param data data to be consumed - //! \return current instance - //! - builder_iface& operator<<(std::string& data); - - //! - //! \return whether the reply could be built - //! - bool reply_ready(void) const; - - //! - //! \return reply object - //! - reply get_reply(void) const; - - //! - //! \return the parsed bulk string - //! - const std::string& get_bulk_string(void) const; - - //! - //! \return whether the bulk string is null - //! - bool is_null(void) const; +//! +//! take data as parameter which is consumed to build the reply +//! every bytes used to build the reply must be removed from the buffer passed as parameter +//! +//! @param data data to be consumed +//! @return current instance +//! + builder_iface& operator<<(std::string& data) override; + +//! +//! @return whether the reply could be built +//! + bool reply_ready() const override; + +//! +//! @return reply object +//! + reply get_reply() const override; + +//! +//! @return the parsed bulk string +//! + const std::string& get_bulk_string() const; + +//! +//! @return whether the bulk string is null +//! + bool is_null() const; private: - void build_reply(void); + void build_reply(); bool fetch_size(std::string& str); void fetch_str(std::string& str); private: - //! - //! builder used to get bulk string size - //! +//! +//! builder used to get bulk string size +//! integer_builder m_int_builder; - //! - //! bulk string size - //! +//! +//! bulk string size +//! int m_str_size; - //! - //! bulk string - //! +//! +//! bulk string +//! std::string m_str; - //! - //! whether the bulk string is null - //! +//! +//! whether the bulk string is null +//! bool m_is_null; - //! - //! whether the reply is ready or not - //! +//! +//! whether the reply is ready or not +//! bool m_reply_ready; - //! - //! reply to be built - //! +//! +//! reply to be built +//! reply m_reply; }; diff --git a/includes/cpp_redis/builders/error_builder.hpp b/includes/cpp_redis/builders/error_builder.hpp index d6962738..79c5c76f 100644 --- a/includes/cpp_redis/builders/error_builder.hpp +++ b/includes/cpp_redis/builders/error_builder.hpp @@ -30,55 +30,74 @@ namespace cpp_redis { namespace builders { -//! -//! builder to build redis error replies -//! +/** + * builder to build redis error replies + * + */ class error_builder : public builder_iface { public: - //! ctor - error_builder(void) = default; - //! dtor - ~error_builder(void) = default; +/** + * ctor + * + */ + error_builder() = default; +/** + * dtor + * + */ + ~error_builder() override = default; - //! copy ctor +/** + * copy ctor + * + */ error_builder(const error_builder&) = delete; - //! assignment operator +/** + * assignment operator + * + */ error_builder& operator=(const error_builder&) = delete; public: - //! - //! take data as parameter which is consumed to build the reply - //! every bytes used to build the reply must be removed from the buffer passed as parameter - //! - //! \param data data to be consumed - //! \return current instance - //! - builder_iface& operator<<(std::string& data); +/** + * take data as parameter which is consumed to build the reply + * every bytes used to build the reply must be removed from the buffer passed as parameter + * + * @param data data to be consumed + * @return current instance + * + */ + builder_iface& operator<<(std::string& data) override; - //! - //! \return whether the reply could be built - //! - bool reply_ready(void) const; +/** + * @return whether the reply could be built + * + */ + bool reply_ready() const override; - //! - //! \return reply object - //! - reply get_reply(void) const; +/** + * @return reply object + * + */ + reply get_reply() const override; - //! - //! \return the parsed error - //! - const std::string& get_error(void) const; +/** + * @return the parsed error + * + */ + const std::string& get_error() const; private: - //! - //! builder used to parse the error - //! +/** + * builder used to parse the error + * + */ simple_string_builder m_string_builder; - //! - //! reply to be built - //! +/** + * reply to be built + * + */ reply m_reply; }; diff --git a/includes/cpp_redis/builders/integer_builder.hpp b/includes/cpp_redis/builders/integer_builder.hpp index 31e0044c..d5bb6914 100644 --- a/includes/cpp_redis/builders/integer_builder.hpp +++ b/includes/cpp_redis/builders/integer_builder.hpp @@ -25,71 +25,92 @@ #include #include -#include +#include namespace cpp_redis { namespace builders { -//! -//! builder to build redis integer replies -//! +/** + * builder to build redis integer replies + * + */ class integer_builder : public builder_iface { public: - //! ctor - integer_builder(void); - //! dtor - ~integer_builder(void) = default; - - //! copy ctor +/** + * ctor + * + */ + integer_builder(); +/** + * dtor + * + */ + ~integer_builder() override = default; + +/** + * copy ctor + * + */ integer_builder(const integer_builder&) = delete; - //! assignment operator +/** + * assignment operator + * + */ integer_builder& operator=(const integer_builder&) = delete; public: - //! - //! take data as parameter which is consumed to build the reply - //! every bytes used to build the reply must be removed from the buffer passed as parameter - //! - //! \param data data to be consumed - //! \return current instance - //! - builder_iface& operator<<(std::string& data); - - //! - //! \return whether the reply could be built - //! - bool reply_ready(void) const; - - //! - //! \return reply object - //! - reply get_reply(void) const; - - //! - //! \return the parsed integer - //! - int64_t get_integer(void) const; +/** + * take data as parameter which is consumed to build the reply + * every bytes used to build the reply must be removed from the buffer passed as parameter + * + * @param data data to be consumed + * @return current instance + * + */ + builder_iface& operator<<(std::string& data) override; + +/** + * @return whether the reply could be built + * + */ + bool reply_ready() const override; + +/** + * @return reply object + * + */ + reply get_reply() const override; + +/** + * @return the parsed integer + * + */ + int64_t get_integer() const; private: - //! - //! parsed number - //! +/** + * parsed number + * + */ int64_t m_nbr; - //! - //! -1 for negative number, 1 otherwise - //! +/** + * -1 for negative number, 1 otherwise + * + */ int64_t m_negative_multiplicator; - //! - //! whether the reply is ready or not - //! +/** + * whether the reply is ready or not + * + */ bool m_reply_ready; - //! - //! reply to be built - //! +/** + * reply to be built + * + */ reply m_reply; }; diff --git a/includes/cpp_redis/builders/reply_builder.hpp b/includes/cpp_redis/builders/reply_builder.hpp index b77e52fd..58f50e19 100644 --- a/includes/cpp_redis/builders/reply_builder.hpp +++ b/includes/cpp_redis/builders/reply_builder.hpp @@ -34,80 +34,103 @@ namespace cpp_redis { namespace builders { -//! -//! class coordinating the several builders and the builder factory to build all the replies returned by redis server -//! +/** + * class coordinating the several builders and the builder factory to build all the replies returned by redis server + * + */ class reply_builder { public: - //! ctor +/** + * ctor + * + */ reply_builder(); - //! dtor +/** + * dtor + * + */ ~reply_builder() = default; - //! copy ctor +/** + * copy ctor + * + */ reply_builder(const reply_builder&) = delete; - //! assignment operator +/** + * assignment operator + * + */ reply_builder& operator=(const reply_builder&) = delete; public: - //! - //! add data to reply builder - //! data is used to build replies that can be retrieved with get_front later on if reply_available returns true - //! - //! \param data data to be used for building replies - //! \return current instance - //! +/** + * add data to reply builder + * data is used to build replies that can be retrieved with get_front later on if reply_available returns true + * + * @param data data to be used for building replies + * @return current instance + * + */ reply_builder& operator<<(const std::string& data); - //! - //! similar as get_front, store reply in the passed parameter - //! - //! \param reply reference to the reply object where to store the first available reply - //! +/** + * similar as get_front, store reply in the passed parameter + * + * @param reply reference to the reply object where to store the first available reply + * + */ void operator>>(reply& reply); - //! - //! \return the first available reply - //! +/** + * @return the first available reply + * + */ const reply& get_front() const; - //! - //! pop the first available reply - //! +/** + * pop the first available reply + * + */ void pop_front(); - //! - //! \return whether a reply is available - //! +/** + * @return whether a reply is available + * + */ bool reply_available() const; - //! - //! reset the reply builder to its initial state (clear internal buffer and stages) - //! +/** + * reset the reply builder to its initial state (clear internal buffer and stages) + * + */ void reset(); private: - //! - //! build reply using m_buffer content - //! - //! \return whether the reply has been fully built or not - //! +/** + * build reply using m_buffer content + * + * @return whether the reply has been fully built or not + * + */ bool build_reply(); private: - //! - //! buffer to be used to build data - //! +/** + * buffer to be used to build data + * + */ std::string m_buffer; - //! - //! current builder used to build current reply - //! +/** + * current builder used to build current reply + * + */ std::unique_ptr m_builder; - //! - //! queue of available (built) replies - //! +/** + * queue of available (built) replies + * + */ std::deque m_available_replies; }; diff --git a/includes/cpp_redis/builders/simple_string_builder.hpp b/includes/cpp_redis/builders/simple_string_builder.hpp index 3924f790..cb0de535 100644 --- a/includes/cpp_redis/builders/simple_string_builder.hpp +++ b/includes/cpp_redis/builders/simple_string_builder.hpp @@ -31,60 +31,80 @@ namespace cpp_redis { namespace builders { -//! -//! builder to build redis simplestring replies -//! +/** + * builder to build redis simple string replies + * + */ class simple_string_builder : public builder_iface { public: - //! ctor +/** + * ctor + * + */ simple_string_builder(); - //! dtor +/** + * dtor + * + */ ~simple_string_builder() override = default; - //! copy ctor +/** + * copy ctor + * + */ simple_string_builder(const simple_string_builder&) = delete; - //! assignment operator +/** + * assignment operator + * + */ simple_string_builder& operator=(const simple_string_builder&) = delete; public: - //! - //! take data as parameter which is consumed to build the reply - //! every bytes used to build the reply must be removed from the buffer passed as parameter - //! - //! \param data data to be consumed - //! \return current instance - //! +/** + * take data as parameter which is consumed to build the reply + * every bytes used to build the reply must be removed from the buffer passed as parameter + * + * @param data data to be consumed + * @return current instance + * + */ builder_iface& operator<<(std::string& data) override; - //! - //! \return whether the reply could be built - //! +/** + * @return whether the reply could be built + * + */ bool reply_ready() const override; - //! - //! \return reply object - //! +/** + * @return reply object + * + */ reply get_reply() const override; - //! - //! \return the parsed simple string - //! +/** + * @return the parsed simple string + * + */ const std::string& get_simple_string() const; private: - //! - //! parsed simple string - //! +/** + * parsed simple string + * + */ std::string m_str; - //! - //! whether the reply is ready or not - //! +/** + * whether the reply is ready or not + * + */ bool m_reply_ready; - //! - //! reply to be built - //! +/** + * reply to be built + * + */ reply m_reply; }; diff --git a/includes/cpp_redis/core/client.hpp b/includes/cpp_redis/core/client.hpp index 67ea6331..62466d63 100644 --- a/includes/cpp_redis/core/client.hpp +++ b/includes/cpp_redis/core/client.hpp @@ -20,7 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#pragma once +#ifndef CPP_REDIS_CORE_CLIENT_HPP_ +#define CPP_REDIS_CORE_CLIENT_HPP_ #include #include @@ -41,17 +42,19 @@ namespace cpp_redis { -//! -//! cpp_redis::client is the class providing communication with a Redis server. -//! It is meant to be used for sending commands to the remote server and receiving its replies. -//! The client support asynchronous requests, as well as synchronous ones. Moreover, commands pipelining is supported. -//! + /** + * cpp_redis::client is the class providing communication with a Redis server. + * It is meant to be used for sending commands to the remote server and receiving its replies. + * The client support asynchronous requests, as well as synchronous ones. Moreover, commands pipelining is supported. + * + */ class client { public: - //! - //! client type - //! used for client kill - //! + /** + * client type + * used for client kill + * + */ enum class client_type { normal, master, @@ -62,39 +65,53 @@ namespace cpp_redis { public: #ifndef __CPP_REDIS_USE_CUSTOM_TCP_CLIENT - //! ctor + /** + * ctor + * + */ client(); #endif /* __CPP_REDIS_USE_CUSTOM_TCP_CLIENT */ - //! - //! custom ctor to specify custom tcp_client - //! - //! \param tcp_client tcp client to be used for network communications - //! + /** + * custom ctor to specify custom tcp_client + * + * @param tcp_client tcp client to be used for network communications + * + */ explicit client(const std::shared_ptr &tcp_client); - //! dtor + /** + * dtor + * + */ ~client(); - //! copy ctor + /** + * copy ctor + * + */ client(const client &) = delete; - //! assignment operator + /** + * assignment operator + * + */ client &operator=(const client &) = delete; public: - //! - //! Connect to redis server - //! - //! \param host host to be connected to - //! \param port port to be connected to - //! \param connect_callback connect handler to be called on connect events (may be null) - //! \param timeout_ms maximum time to connect - //! \param max_reconnects maximum attempts of reconnection if connection dropped - //! \param reconnect_interval_ms time between two attempts of reconnection - //! + /** + * Connect to redis server + * + * @param host host to be connected to + * @param port port to be connected to + * @param connect_callback connect handler to be called on connect events (may be null) + * @param timeout_ms maximum time to connect + * @param max_reconnects maximum attempts of reconnection if connection dropped + * @param reconnect_interval_ms time between two attempts of reconnection + * + */ void connect( const std::string &host = "127.0.0.1", std::size_t port = 6379, @@ -103,15 +120,16 @@ namespace cpp_redis { std::int32_t max_reconnects = 0, std::uint32_t reconnect_interval_ms = 0); - //! - //! Connect to redis server - //! - //! \param name sentinel name - //! \param connect_callback connect handler to be called on connect events (may be null) - //! \param timeout_ms maximum time to connect - //! \param max_reconnects maximum attempts of reconnection if connection dropped - //! \param reconnect_interval_ms time between two attempts of reconnection - //! + /** + * Connect to redis server + * + * @param name sentinel name + * @param connect_callback connect handler to be called on connect events (may be null) + * @param timeout_ms maximum time to connect + * @param max_reconnects maximum attempts of reconnection if connection dropped + * @param reconnect_interval_ms time between two attempts of reconnection + * + */ void connect( const std::string &name, const connect_callback_t &connect_callback = nullptr, @@ -119,84 +137,97 @@ namespace cpp_redis { std::int32_t max_reconnects = 0, std::uint32_t reconnect_interval_ms = 0); - //! - //! \return whether we are connected to the redis server - //! + /** + * @return whether we are connected to the redis server + * + */ bool is_connected() const; - //! - //! disconnect from redis server - //! - //! \param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. - //! + /** + * disconnect from redis server + * + * @param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. + * + */ void disconnect(bool wait_for_removal = false); - //! - //! \return whether an attempt to reconnect is in progress - //! + /** + * @return whether an attempt to reconnect is in progress + * + */ bool is_reconnecting() const; - //! - //! stop any reconnect in progress - //! + /** + * stop any reconnect in progress + * + */ void cancel_reconnect(); public: - //! - //! reply callback called whenever a reply is received - //! takes as parameter the received reply - //! + /** + * reply callback called whenever a reply is received + * takes as parameter the received reply + * + */ typedef std::function reply_callback_t; - //! - //! send the given command - //! the command is actually pipelined and only buffered, so nothing is sent to the network - //! please call commit() / sync_commit() to flush the buffer - //! - //! \param redis_cmd command to be sent - //! \param callback callback to be called on received reply - //! \return current instance - //! + /** + * send the given command + * the command is actually pipelined and only buffered, so nothing is sent to the network + * please call commit() / sync_commit() to flush the buffer + * + * @param redis_cmd command to be sent + * @param callback callback to be called on received reply + * @return current instance + * + */ client &send(const std::vector &redis_cmd, const reply_callback_t &callback); - //! - //! same as the other send method - //! but future based: does not take any callback and return an std:;future to handle the reply - //! - //! \param redis_cmd command to be sent - //! \return std::future to handler redis reply - //! + /** + * same as the other send method + * but future based: does not take any callback and return an std:;future to handle the reply + * + * @param redis_cmd command to be sent + * @return std::future to handler redis reply + * + */ std::future send(const std::vector &redis_cmd); - //! - //! Sends all the commands that have been stored by calling send() since the last commit() call to the redis server. - //! That is, pipelining is supported in a very simple and efficient way: client.send(...).send(...).send(...).commit() will send the 3 commands at once (instead of sending 3 network requests, one for each command, as it would have been done without pipelining). - //! Pipelined commands are always removed from the buffer, even in the case of an error (for example, calling commit while the client is not connected, something that throws an exception). - //! commit() works asynchronously: it returns immediately after sending the queued requests and replies are processed asynchronously. - //! - //! Please note that, while commit() can safely be called from inside a reply callback, calling sync_commit() from inside a reply callback is not permitted and will lead to undefined behavior, mostly deadlock. - //! + /** + * Sends all the commands that have been stored by calling send() since the last commit() call to the redis server. + * That is, pipelining is supported in a very simple and efficient way: client.send(...).send(...).send(...).commit() will send the 3 commands at once (instead of sending 3 network requests, one for each command, as it would have been done without pipelining). + * Pipelined commands are always removed from the buffer, even in the case of an error (for example, calling commit while the client is not connected, something that throws an exception). + * commit() works asynchronously: it returns immediately after sending the queued requests and replies are processed asynchronously. + * + * Please note that, while commit() can safely be called from inside a reply callback, calling sync_commit() from inside a reply callback is not permitted and will lead to undefined behavior, mostly deadlock. + * + */ client &commit(); - //! - //! same as commit(), but synchronous - //! will block until all pending commands have been sent and that a reply has been received for each of them and all underlying callbacks completed - //! - //! \return current instance - //! + /** + * same as commit(), but synchronous + * will block until all pending commands have been sent and that a reply has been received for each of them and all underlying callbacks completed + * + * @return current instance + * + */ client &sync_commit(); - //! - //! same as sync_commit, but with a timeout - //! will simply block until it completes or timeout expires - //! - //! \return current instance - //! + /** + * same as sync_commit, but with a timeout + * will simply block until it completes or timeout expires + * + * @return current instance + * + */ template client & sync_commit(const std::chrono::duration &timeout) { - //! no need to call commit in case of reconnection - //! the reconnection flow will do it for us + /** + * no need to call commit in case of reconnection + * the reconnection flow will do it for us + * + */ if (!is_reconnecting()) { try_commit(); } @@ -214,101 +245,115 @@ namespace cpp_redis { } private: - //! - //! \return whether a reconnection attempt should be performed - //! + /** + * @return whether a reconnection attempt should be performed + * + */ bool should_reconnect() const; - //! - //! resend all pending commands that failed to be sent due to disconnection - //! + /** + * resend all pending commands that failed to be sent due to disconnection + * + */ void resend_failed_commands(); - //! - //! sleep between two reconnect attempts if necessary - //! + /** + * sleep between two reconnect attempts if necessary + * + */ void sleep_before_next_reconnect_attempt(); - //! - //! reconnect to the previously connected host - //! automatically re authenticate and resubscribe to subscribed channel in case of success - //! +/** + * reconnect to the previously connected host + * automatically re authenticate and resubscribe to subscribed channel in case of success + * + */ void reconnect(); - //! - //! re authenticate to redis server based on previously used password - //! +/** + * re authenticate to redis server based on previously used password + * + */ void re_auth(); - //! - //! re select db to redis server based on previously selected db - //! +/** + * re select db to redis server based on previously selected db + * + */ void re_select(); private: - //! - //! unprotected send - //! same as send, but without any mutex lock - //! - //! \param redis_cmd cmd to be sent - //! \param callback callback to be called whenever a reply is received - //! +/** + * unprotected send + * same as send, but without any mutex lock + * + * @param redis_cmd cmd to be sent + * @param callback callback to be called whenever a reply is received + * + */ void unprotected_send(const std::vector &redis_cmd, const reply_callback_t &callback); - //! - //! unprotected auth - //! same as auth, but without any mutex lock - //! - //! \param password password to be used for authentication - //! \param reply_callback callback to be called whenever a reply is received - //! +/** + * unprotected auth + * same as auth, but without any mutex lock + * + * @param password password to be used for authentication + * @param reply_callback callback to be called whenever a reply is received + * + */ void unprotected_auth(const std::string &password, const reply_callback_t &reply_callback); - //! - //! unprotected select - //! same as select, but without any mutex lock - //! - //! \param index index to be used for db select - //! \param reply_callback callback to be called whenever a reply is received - //! +/** + * unprotected select + * same as select, but without any mutex lock + * + * @param index index to be used for db select + * @param reply_callback callback to be called whenever a reply is received + * + */ void unprotected_select(int index, const reply_callback_t &reply_callback); public: - //! - //! add a sentinel definition. Required for connect() or get_master_addr_by_name() when autoconnect is enabled. - //! - //! \param host sentinel host - //! \param port sentinel port - //! \param timeout_ms maximum time to connect - //! +/** + * add a sentinel definition. Required for connect() or get_master_addr_by_name() when autoconnect is enabled. + * + * @param host sentinel host + * @param port sentinel port + * @param timeout_ms maximum time to connect + * + */ void add_sentinel(const std::string &host, std::size_t port, std::uint32_t timeout_ms = 0); - //! - //! retrieve sentinel for current client - //! - //! \return sentinel associated to current client - //! +/** + * retrieve sentinel for current client + * + * @return sentinel associated to current client + * + */ const sentinel &get_sentinel() const; - //! - //! retrieve sentinel for current client - //! non-const version - //! - //! \return sentinel associated to current client - //! +/** + * retrieve sentinel for current client + * non-const version + * + * @return sentinel associated to current client + * + */ sentinel &get_sentinel(); - //! - //! clear all existing sentinels. - //! +/** + * clear all existing sentinels. + * + */ void clear_sentinels(); public: - //! - //! aggregate method to be used for some commands (like zunionstore) - //! these match the aggregate methods supported by redis - //! use server_default if you are not willing to specify this parameter and let the server defaults - //! +/** + * aggregate method to be used for some commands (like zunionstore) + * these match the aggregate methods supported by redis + * use server_default if you are not willing to specify this parameter and let the server defaults + * + */ enum class aggregate_method { sum, min, @@ -316,19 +361,21 @@ namespace cpp_redis { server_default }; - //! - //! convert an aggregate_method enum to its equivalent redis-server string - //! - //! \param method aggregate_method to convert - //! \return conversion - //! +/** + * convert an aggregate_method enum to its equivalent redis-server string + * + * @param method aggregate_method to convert + * @return conversion + * + */ std::string aggregate_method_to_string(aggregate_method method) const; public: - //! - //! geographic unit to be used for some commands (like georadius) - //! these match the geo units supported by redis-server - //! +/** + * geographic unit to be used for some commands (like georadius) + * these match the geo units supported by redis-server + * + */ enum class geo_unit { m, km, @@ -336,20 +383,22 @@ namespace cpp_redis { mi }; - //! - //! convert a geo unit to its equivalent redis-server string - //! - //! \param unit geo_unit to convert - //! \return conversion - //! +/** + * convert a geo unit to its equivalent redis-server string + * + * @param unit geo_unit to convert + * @return conversion + * + */ std::string geo_unit_to_string(geo_unit unit) const; public: - //! - //! overflow type to be used for some commands (like bitfield) - //! these match the overflow types supported by redis-server - //! use server_default if you are not willing to specify this parameter and let the server defaults - //! +/** + * overflow type to be used for some commands (like bitfield) + * these match the overflow types supported by redis-server + * use server_default if you are not willing to specify this parameter and let the server defaults + * + */ enum class overflow_type { wrap, sat, @@ -357,96 +406,108 @@ namespace cpp_redis { server_default }; - //! - //! convert an overflow type to its equivalent redis-server string - //! - //! \param type overflow type to convert - //! \return conversion - //! +/** + * convert an overflow type to its equivalent redis-server string + * + * @param type overflow type to convert + * @return conversion + * + */ std::string overflow_type_to_string(overflow_type type) const; public: - //! - //! bitfield operation type to be used for some commands (like bitfield) - //! these match the bitfield operation types supported by redis-server - //! +/** + * bitfield operation type to be used for some commands (like bitfield) + * these match the bitfield operation types supported by redis-server + * + */ enum class bitfield_operation_type { get, set, incrby }; - //! - //! convert a bitfield operation type to its equivalent redis-server string - //! - //! \param operation operation type to convert - //! \return conversion - //! +/** + * convert a bitfield operation type to its equivalent redis-server string + * + * @param operation operation type to convert + * @return conversion + * + */ std::string bitfield_operation_type_to_string(bitfield_operation_type operation) const; public: - //! - //! used to store a get, set or incrby bitfield operation (for bitfield command) - //! +/** + * used to store a get, set or incrby bitfield operation (for bitfield command) + * + */ struct bitfield_operation { - //! - //! operation type (get, set, incrby) - //! +/** + * operation type (get, set, incrby) + * + */ bitfield_operation_type operation_type; - //! - //! redis type parameter for get, set or incrby operations - //! +/** + * redis type parameter for get, set or incrby operations + * + */ std::string type; - //! - //! redis offset parameter for get, set or incrby operations - //! +/** + * redis offset parameter for get, set or incrby operations + * + */ int offset; - //! - //! redis value parameter for set operation, or increment parameter for incrby operation - //! +/** + * redis value parameter for set operation, or increment parameter for incrby operation + * + */ int value; - //! - //! overflow optional specification - //! +/** + * overflow optional specification + * + */ overflow_type overflow; - //! - //! build a bitfield_operation for a bitfield get operation - //! - //! \param type type param of a get operation - //! \param offset offset param of a get operation - //! \param overflow overflow specification (leave to server_default if you do not want to specify it) - //! \return corresponding get bitfield_operation - //! +/** + * build a bitfield_operation for a bitfield get operation + * + * @param type type param of a get operation + * @param offset offset param of a get operation + * @param overflow overflow specification (leave to server_default if you do not want to specify it) + * @return corresponding get bitfield_operation + * + */ static bitfield_operation get(const std::string &type, int offset, overflow_type overflow = overflow_type::server_default); - //! - //! build a bitfield_operation for a bitfield set operation - //! - //! \param type type param of a set operation - //! \param offset offset param of a set operation - //! \param value value param of a set operation - //! \param overflow overflow specification (leave to server_default if you do not want to specify it) - //! \return corresponding set bitfield_operation - //! +/** + * build a bitfield_operation for a bitfield set operation + * + * @param type type param of a set operation + * @param offset offset param of a set operation + * @param value value param of a set operation + * @param overflow overflow specification (leave to server_default if you do not want to specify it) + * @return corresponding set bitfield_operation + * + */ static bitfield_operation set(const std::string &type, int offset, int value, overflow_type overflow = overflow_type::server_default); - //! - //! build a bitfield_operation for a bitfield incrby operation - //! - //! \param type type param of a incrby operation - //! \param offset offset param of a incrby operation - //! \param increment increment param of a incrby operation - //! \param overflow overflow specification (leave to server_default if you do not want to specify it) - //! \return corresponding incrby bitfield_operation - //! +/** + * build a bitfield_operation for a bitfield incrby operation + * + * @param type type param of a incrby operation + * @param offset offset param of a incrby operation + * @param increment increment param of a incrby operation + * @param overflow overflow specification (leave to server_default if you do not want to specify it) + * @return corresponding incrby bitfield_operation + * + */ static bitfield_operation incrby(const std::string &type, int offset, int increment, overflow_type overflow = overflow_type::server_default); }; @@ -1451,14 +1512,17 @@ namespace cpp_redis { std::future xadd(const std::string &key, const std::string &id, const std::multimap &field_members); - //! \brief changes the ownership of a pending message to the specified consumer - //! \param stream - //! \param group - //! \param consumer - //! \param min_idle_time - //! \param message_ids - //! \param reply_callback - //! \return +/** + * @brief changes the ownership of a pending message to the specified consumer + * @param stream + * @param group + * @param consumer + * @param min_idle_time + * @param message_ids + * @param reply_callback + * @return + * + */ client &xclaim(const std::string &stream, const std::string &group, const std::string &consumer, int min_idle_time, const std::vector &message_ids, const xclaim_options_t &options, @@ -1625,7 +1689,10 @@ namespace cpp_redis { std::future xtrim(const std::string &key, int max_len); - //! optimizes the xtrim command +/** + * optimizes the xtrim command + * + */ client &xtrim_approx(const std::string &key, int max_len, const reply_callback_t &reply_callback); std::future xtrim_approx(const std::string &key, int max_len); @@ -2037,7 +2104,10 @@ namespace cpp_redis { std::vector weights, aggregate_method method); private: - //! client kill impl +/** + * client kill impl + * + */ template typename std::enable_if::value>::type client_kill_unpack_arg(std::vector &redis_cmd, reply_callback_t &, client_type type); @@ -2063,155 +2133,198 @@ namespace cpp_redis { client_kill_impl(std::vector &redis_cmd, reply_callback_t &reply, const T &arg); private: - //! sort impl +/** + * sort impl + * + */ client & sort(const std::string &key, const std::string &by_pattern, bool limit, std::size_t offset, std::size_t count, const std::vector &get_patterns, bool asc_order, bool alpha, const std::string &store_dest, const reply_callback_t &reply_callback); - //! zrevrangebyscore impl +/** + * zrevrangebyscore impl + * + */ client &zrevrangebyscore(const std::string &key, const std::string &max, const std::string &min, bool limit, std::size_t offset, std::size_t count, bool withscores, const reply_callback_t &reply_callback); - //! zrangebyscore impl +/** + * zrangebyscore impl + * + */ client &zrangebyscore(const std::string &key, const std::string &min, const std::string &max, bool limit, std::size_t offset, std::size_t count, bool withscores, const reply_callback_t &reply_callback); - //! zrevrangebylex impl +/** + * zrevrangebylex impl + * + */ client &zrevrangebylex(const std::string &key, const std::string &max, const std::string &min, bool limit, std::size_t offset, std::size_t count, bool withscores, const reply_callback_t &reply_callback); - //! zrangebylex impl +/** + * zrangebylex impl + * + */ client &zrangebylex(const std::string &key, const std::string &min, const std::string &max, bool limit, std::size_t offset, std::size_t count, bool withscores, const reply_callback_t &reply_callback); private: - //! - //! redis connection receive handler, triggered whenever a reply has been read by the redis connection - //! - //! \param connection redis_connection instance - //! \param reply parsed reply - //! +/** + * redis connection receive handler, triggered whenever a reply has been read by the redis connection + * + * @param connection redis_connection instance + * @param reply parsed reply + * + */ void connection_receive_handler(network::redis_connection &connection, reply &reply); - //! - //! redis_connection disconnection handler, triggered whenever a disconnection occurred - //! - //! \param connection redis_connection instance - //! +/** + * redis_connection disconnection handler, triggered whenever a disconnection occurred + * + * @param connection redis_connection instance + * + */ void connection_disconnection_handler(network::redis_connection &connection); - //! - //! reset the queue of pending callbacks - //! +/** + * reset the queue of pending callbacks + * + */ void clear_callbacks(); - //! - //! try to commit the pending pipelined - //! if client is disconnected, will throw an exception and clear all pending callbacks (call clear_callbacks()) - //! +/** + * try to commit the pending pipelined + * if client is disconnected, will throw an exception and clear all pending callbacks (call clear_callbacks()) + * + */ void try_commit(); - //! Execute a command on the client and tie the callback to a future +/** + * Execute a command on the client and tie the callback to a future + * + */ std::future exec_cmd(const std::function &f); private: - //! - //! struct to store commands information (command to be sent and callback to be called) - //! +/** + * struct to store commands information (command to be sent and callback to be called) + * + */ struct command_request { std::vector command; reply_callback_t callback; }; private: - //! - //! server we are connected to - //! +/** + * server we are connected to + * + */ std::string m_redis_server; - //! - //! port we are connected to - //! +/** + * port we are connected to + * + */ std::size_t m_redis_port = 0; - //! - //! master name (if we are using sentinel) we are connected to - //! +/** + * master name (if we are using sentinel) we are connected to + * + */ std::string m_master_name; - //! - //! password used to authenticate - //! +/** + * password used to authenticate + * + */ std::string m_password; - //! - //! selected redis db - //! +/** + * selected redis db + * + */ int m_database_index = 0; - //! - //! tcp client for redis connection - //! +/** + * tcp client for redis connection + * + */ network::redis_connection m_client; - //! - //! redis sentinel - //! +/** + * redis sentinel + * + */ cpp_redis::sentinel m_sentinel; - //! - //! max time to connect - //! +/** + * max time to connect + * + */ std::uint32_t m_connect_timeout_ms = 0; - //! - //! max number of reconnection attempts - //! +/** + * max number of reconnection attempts + * + */ std::int32_t m_max_reconnects = 0; - //! - //! current number of attempts to reconnect - //! +/** + * current number of attempts to reconnect + * + */ std::int32_t m_current_reconnect_attempts = 0; - //! - //! time between two reconnection attempts - //! +/** + * time between two reconnection attempts + * + */ std::uint32_t m_reconnect_interval_ms = 0; - //! - //! reconnection status - //! +/** + * reconnection status + * + */ std::atomic_bool m_reconnecting; - //! - //! to force cancel reconnection - //! +/** + * to force cancel reconnection + * + */ std::atomic_bool m_cancel; - //! - //! sent commands waiting to be executed - //! +/** + * sent commands waiting to be executed + * + */ std::queue m_commands; - //! - //! user defined connect status callback - //! +/** + * user defined connect status callback + * + */ connect_callback_t m_connect_callback; - //! - //! callbacks thread safety - //! +/** + * callbacks thread safety + * + */ std::mutex m_callbacks_mutex; - //! - //! condvar for callbacks updates - //! +/** + * condvar for callbacks updates + * + */ std::condition_variable m_sync_condvar; - //! - //! number of callbacks currently being running - //! +/** + * number of callbacks currently being running + * + */ std::atomic m_callbacks_running; }; // namespace cpp_redis } // namespace cpp_redis #include + +#endif diff --git a/includes/cpp_redis/core/consumer.hpp b/includes/cpp_redis/core/consumer.hpp index e21b4947..e6c71022 100644 --- a/includes/cpp_redis/core/consumer.hpp +++ b/includes/cpp_redis/core/consumer.hpp @@ -29,36 +29,60 @@ namespace cpp_redis { - //! - //! reply callback called whenever a reply is received - //! takes as parameter the received reply - //! + using defer = std::shared_ptr; + +/** + * reply callback called whenever a reply is received + * takes as parameter the received reply + */ typedef dispatch_callback_t consumer_callback_t; + typedef client::reply_callback_t reply_callback_t; + typedef struct consumer_callback_container { consumer_callback_t consumer_callback; acknowledgement_callback_t acknowledgement_callback; } consumer_callback_container_t; - typedef std::map consumer_queue_t; + typedef struct consumer_reply { + std::string group_id; + xstream_reply_t reply; + } consumer_reply_t; + + class consumer_client_container { + public: + consumer_client_container(); + + client ack_client; + client poll_client; + }; + + typedef consumer_client_container consumer_client_container_t; + + typedef std::unique_ptr client_container_ptr_t; + + typedef std::multimap consumer_callbacks_t; + + //typedef std::map consumer_callbacks_t; class consumer { public: - explicit consumer(std::string stream, std::string consumer, size_t max_concurrency = std::thread::hardware_concurrency()); + explicit consumer(std::string stream, std::string consumer, + size_t max_concurrency = std::thread::hardware_concurrency()); consumer &subscribe(const std::string &group, const consumer_callback_t &consumer_callback, const acknowledgement_callback_t &acknowledgement_callback = nullptr); - void process(); - - //! \brief Connect to redis server - //! \param host host to be connected to - //! \param port port to be connected to - //! \param connect_callback connect handler to be called on connect events (may be null) - //! \param timeout_ms maximum time to connect - //! \param max_reconnects maximum attempts of reconnection if connection dropped - //! \param reconnect_interval_ms time between two attempts of reconnection + /** + * @brief Connect to redis server + * @param host host to be connected to + * @param port port to be connected to + * @param connect_callback connect handler to be called on connect events (may be null) + * @param timeout_ms maximum time to connect + * @param max_reconnects maximum attempts of reconnection if connection dropped + * @param reconnect_interval_ms time between two attempts of reconnection + */ void connect( const std::string &host = "127.0.0.1", std::size_t port = 6379, @@ -67,34 +91,42 @@ namespace cpp_redis { std::int32_t max_reconnects = 0, std::uint32_t reconnect_interval_ms = 0); - //! - //! commit pipelined transaction - //! that is, send to the network all commands pipelined by calling send() / subscribe() / ... - //! - //! \return current instance - //! + void auth(const std::string &password, + const reply_callback_t &reply_callback = nullptr); + + /* + * commit pipelined transaction + * that is, send to the network all commands pipelined by calling send() / subscribe() / ... + * + * @return current instance + */ consumer &commit(); + void dispatch_changed_handler(size_t size); + + private: + void poll(); + private: std::string m_stream; std::string m_name; + std::string m_read_id; + int m_block_sec; size_t m_max_concurrency; - std::shared_ptr m_client; - std::shared_ptr m_sub_client; - consumer_queue_t m_task_queue; - std::mutex m_task_queue_mutex; - std::shared_ptr m_proc_queue; + int m_read_count; + + client_container_ptr_t m_client; - std::mutex m_reply_queue_mutex; - std::queue m_reply_queue; + consumer_callbacks_t m_callbacks; + std::mutex m_callbacks_mutex; - std::mutex m_q_status_mutex; - std::condition_variable m_q_status; - //dispatch_queue_t m_proc_queue; + dispatch_queue_ptr_t m_dispatch_queue; + std::atomic_bool dispatch_queue_full{false}; + std::condition_variable dispatch_queue_changed; + std::mutex dispatch_queue_changed_mutex; bool is_ready = false; - std::condition_variable m_cv; - std::mutex m_cv_mutex; + std::atomic_bool m_should_read_pending{true}; }; } // namespace cpp_redis diff --git a/includes/cpp_redis/core/reply.hpp b/includes/cpp_redis/core/reply.hpp index 7c70267f..3233f0de 100644 --- a/includes/cpp_redis/core/reply.hpp +++ b/includes/cpp_redis/core/reply.hpp @@ -32,10 +32,11 @@ namespace cpp_redis { -//! -//! cpp_redis::reply is the class that wraps Redis server replies. -//! That is, cpp_redis::reply objects are passed as parameters of commands callbacks and contain the server's response. -//! +/** + * cpp_redis::reply is the class that wraps Redis server replies. + * That is, cpp_redis::reply objects are passed as parameters of commands callbacks and contain the server's response. + * + */ class reply { public: #define __CPP_REDIS_REPLY_ERR 0 @@ -45,9 +46,10 @@ namespace cpp_redis { #define __CPP_REDIS_REPLY_INT 4 #define __CPP_REDIS_REPLY_ARRAY 5 - //! - //! type of reply, based on redis server standard replies - //! +/** + * type of reply, based on redis server standard replies + * + */ enum class type { error = __CPP_REDIS_REPLY_ERR, bulk_string = __CPP_REDIS_REPLY_BULK, @@ -57,9 +59,10 @@ namespace cpp_redis { array = __CPP_REDIS_REPLY_ARRAY }; - //! - //! specific type of replies for string-based replies - //! +/** + * specific type of replies for string-based replies + * + */ enum class string_type { error = __CPP_REDIS_REPLY_ERR, bulk_string = __CPP_REDIS_REPLY_BULK, @@ -67,170 +70,206 @@ namespace cpp_redis { }; public: - //! - //! default ctor (set a null reply) - //! +/** + * default ctor (set a null reply) + * + */ reply(); - //! - //! ctor for string values - //! - //! \param value string value - //! \param reply_type of string reply - //! +/** + * ctor for string values + * + * @param value string value + * @param reply_type of string reply + * + */ reply(const std::string &value, string_type reply_type); - //! - //! ctor for int values - //! - //! \param value integer value - //! +/** + * ctor for int values + * + * @param value integer value + * + */ explicit reply(int64_t value); - //! - //! ctor for array values - //! - //! \param rows array reply - //! \return current instance - //! +/** + * ctor for array values + * + * @param rows array reply + * @return current instance + * + */ explicit reply(const std::vector &rows); - //! dtor +/** + * dtor + * + */ ~reply() = default; - //! copy ctor +/** + * copy ctor + * + */ reply(const reply &) = default; - //! assignment operator +/** + * assignment operator + * + */ reply &operator=(const reply &) = default; - //! move ctor +/** + * move ctor + * + */ reply(reply &&) noexcept; - //! move assignment operator +/** + * move assignment operator + * + */ reply &operator=(reply &&) noexcept; public: - //! - //! \return whether the reply is an array - //! +/** + * @return whether the reply is an array + * + */ bool is_array() const; - //! - //! \return whether the reply is a string (simple, bulk, error) - //! +/** + * @return whether the reply is a string (simple, bulk, error) + * + */ bool is_string() const; - //! - //! \return whether the reply is a simple string - //! +/** + * @return whether the reply is a simple string + * + */ bool is_simple_string() const; - //! - //! \return whether the reply is a bulk string - //! +/** + * @return whether the reply is a bulk string + * + */ bool is_bulk_string() const; - //! - //! \return whether the reply is an error - //! +/** + * @return whether the reply is an error + * + */ bool is_error() const; - //! - //! \return whether the reply is an integer - //! +/** + * @return whether the reply is an integer + * + */ bool is_integer() const; - //! - //! \return whether the reply is null - //! +/** + * @return whether the reply is null + * + */ bool is_null() const; public: - //! - //! \return true if function is not an error - //! +/** + * @return true if function is not an error + * + */ bool ok() const; - //! - //! \return true if function is an error - //! +/** + * @return true if function is an error + * + */ bool ko() const; - //! - //! convenience implicit conversion, same as !is_null() / ok() - //! +/** + * convenience implicit conversion, same as !is_null() / ok() + * + */ explicit operator bool() const; public: optional try_get_int() const; public: - //! - //! \return the underlying error - //! +/** + * @return the underlying error + * + */ const std::string &error() const; - //! - //! \return the underlying array - //! +/** + * @return the underlying array + * + */ const std::vector &as_array() const; - //! - //! \return the underlying string - //! +/** + * @return the underlying string + * + */ const std::string &as_string() const; - //! - //! \return the underlying integer - //! +/** + * @return the underlying integer + * + */ int64_t as_integer() const; public: - //! - //! set reply as null - //! +/** + * set reply as null + * + */ void set(); - //! - //! set a string reply - //! - //! \param value string value - //! \param reply_type of string reply - //! +/** + * set a string reply + * + * @param value string value + * @param reply_type of string reply + * + */ void set(const std::string &value, string_type reply_type); - //! - //! set an integer reply - //! - //! \param value integer value - //! +/** + * set an integer reply + * + * @param value integer value + * + */ void set(int64_t value); - //! - //! set an array reply - //! - //! \param rows array reply - //! +/** + * set an array reply + * + * @param rows array reply + * + */ void set(const std::vector &rows); - //! - //! for array replies, add a new row to the reply - //! - //! \param reply new row to be appended - //! \return current instance - //! +/** + * for array replies, add a new row to the reply + * + * @param reply new row to be appended + * @return current instance + * + */ reply &operator<<(const reply &reply); public: - //! - //! \return reply type - //! +/** + * @return reply type + * + */ type get_type() const; - //! support for output - friend std::ostream &operator<<(std::ostream &os, const reply &reply); - private: type m_type; std::vector m_rows; @@ -242,5 +281,8 @@ namespace cpp_redis { } // namespace cpp_redis -//! support for output +/** + * support for output + * + */ std::ostream &operator<<(std::ostream &os, const cpp_redis::reply_t &reply); diff --git a/includes/cpp_redis/core/sentinel.hpp b/includes/cpp_redis/core/sentinel.hpp index 57813096..21d9190c 100644 --- a/includes/cpp_redis/core/sentinel.hpp +++ b/includes/cpp_redis/core/sentinel.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -32,316 +33,388 @@ namespace cpp_redis { -//! -//! cpp_redis::sentinel is the class providing sentinel configuration. -//! It is meant to be used for sending sentinel-related commands to the remote server and receiving its replies. -//! It is also meant to be used with cpp_redis::client and cpp_redis::subscriber for high availability (automatic failover if reconnection is enabled). -//! -class sentinel { -public: -//! ctor & dtor +/** + * cpp_redis::sentinel is the class providing sentinel configuration. + * It is meant to be used for sending sentinel-related commands to the remote server and receiving its replies. + * It is also meant to be used with cpp_redis::client and cpp_redis::subscriber for high availability (automatic failover if reconnection is enabled). + * + */ + class sentinel { + public: +/** + * ctor & dtor + * + */ #ifndef __CPP_REDIS_USE_CUSTOM_TCP_CLIENT - //! default ctor - sentinel(void); + + /** + * default ctor + * + */ + sentinel(); + #endif /* __CPP_REDIS_USE_CUSTOM_TCP_CLIENT */ - //! - //! custom ctor to specify custom tcp_client - //! - //! \param tcp_client tcp client to be used for network communications - //! - explicit sentinel(const std::shared_ptr& tcp_client); - - //! dtor - ~sentinel(void); - - //! copy ctor - sentinel(const sentinel&) = delete; - //! assignment operator - sentinel& operator=(const sentinel&) = delete; - -public: - //! - //! callback to be called whenever a reply has been received - //! - typedef std::function reply_callback_t; - - //! - //! send the given command - //! the command is actually pipelined and only buffered, so nothing is sent to the network - //! please call commit() to flush the buffer - //! - //! \param sentinel_cmd command to be sent - //! \param callback callback to be called when reply is received for this command - //! \return current instance - //! - sentinel& send(const std::vector& sentinel_cmd, const reply_callback_t& callback = nullptr); - - //! - //! commit pipelined transaction - //! that is, send to the network all commands pipelined by calling send() - //! - //! \return current instance - //! - sentinel& commit(void); - - //! - //! same as commit(), but synchronous - //! will block until all pending commands have been sent and that a reply has been received for each of them and all underlying callbacks completed - //! - //! \return current instance - //! - sentinel& sync_commit(void); - - //! - //! same as sync_commit, but with a timeout - //! will simply block until it completes or timeout expires - //! - //! \return current instance - //! - template - sentinel& - sync_commit(const std::chrono::duration& timeout) { - try_commit(); - - std::unique_lock lock_callback(m_callbacks_mutex); - __CPP_REDIS_LOG(debug, "cpp_redis::sentinel waiting for callbacks to complete"); - if (!m_sync_condvar.wait_for(lock_callback, timeout, [=] { - return m_callbacks_running == 0 && m_callbacks.empty(); - })) { - __CPP_REDIS_LOG(debug, "cpp_redis::sentinel finished waiting for callback"); - } - else { - __CPP_REDIS_LOG(debug, "cpp_redis::sentinel timed out waiting for callback"); - } - return *this; - } - -public: - //! - //! add a sentinel definition. Required for connect() or get_master_addr_by_name() when autoconnect is enabled. - //! - //! \param host sentinel host - //! \param port sentinel port - //! \param timeout_ms maximum time to connect - //! \return current instance - //! - sentinel& add_sentinel(const std::string& host, std::size_t port, std::uint32_t timeout_ms = 0); - - //! - //! clear all existing sentinels. - //! - void clear_sentinels(void); - -public: - //! - //! disconnect from redis server - //! - //! \param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. - //! - void disconnect(bool wait_for_removal = false); - - //! - //! \return whether we are connected to the redis server or not - //! - bool is_connected(void); - - //! - //! handlers called whenever disconnection occurred - //! function takes the sentinel current instance as parameter - //! - typedef std::function sentinel_disconnect_handler_t; - - //! - //! Connect to 1st active sentinel we find. Requires add_sentinel() to be called first - //! will use timeout set for each added sentinel independently - //! - //! \param disconnect_handler handler to be called whenever disconnection occurs - //! - void connect_sentinel(const sentinel_disconnect_handler_t& disconnect_handler = nullptr); - - //! - //! Connect to named sentinel - //! - //! \param host host to be connected to - //! \param port port to be connected to - //! \param timeout_ms maximum time to connect - //! \param disconnect_handler handler to be called whenever disconnection occurs - //! - void connect( - const std::string& host, - std::size_t port, - const sentinel_disconnect_handler_t& disconnect_handler = nullptr, - std::uint32_t timeout_ms = 0); - - //! - //! Used to find the current redis master by asking one or more sentinels. Use high availability. - //! Handles connect() and disconnect() automatically when autoconnect=true - //! This method is synchronous. No need to call sync_commit() or process a reply callback. - //! Call add_sentinel() before using when autoconnect==true - //! - //! \param name sentinel name - //! \param host sentinel host - //! \param port sentinel port - //! \param autoconnect autoconnect we loop through and connect/disconnect as necessary to sentinels that were added using add_sentinel(). - //! Otherwise we rely on the call to connect to a sentinel before calling this method. - //! \return true if a master was found and fills in host and port output parameters, false otherwise - //! - bool get_master_addr_by_name( - const std::string& name, - std::string& host, - std::size_t& port, - bool autoconnect = true); - -public: - sentinel& ckquorum(const std::string& name, const reply_callback_t& reply_callback = nullptr); - sentinel& failover(const std::string& name, const reply_callback_t& reply_callback = nullptr); - sentinel& flushconfig(const reply_callback_t& reply_callback = nullptr); - sentinel& master(const std::string& name, const reply_callback_t& reply_callback = nullptr); - sentinel& masters(const reply_callback_t& reply_callback = nullptr); - sentinel& monitor(const std::string& name, const std::string& ip, std::size_t port, std::size_t quorum, const reply_callback_t& reply_callback = nullptr); - sentinel& ping(const reply_callback_t& reply_callback = nullptr); - sentinel& remove(const std::string& name, const reply_callback_t& reply_callback = nullptr); - sentinel& reset(const std::string& pattern, const reply_callback_t& reply_callback = nullptr); - sentinel& sentinels(const std::string& name, const reply_callback_t& reply_callback = nullptr); - sentinel& set(const std::string& name, const std::string& option, const std::string& value, const reply_callback_t& reply_callback = nullptr); - sentinel& slaves(const std::string& name, const reply_callback_t& reply_callback = nullptr); - -public: - //! - //! store informations related to a sentinel - //! typically, host, port and connection timeout - //! - class sentinel_def { - public: - //! ctor - sentinel_def(const std::string& host, std::size_t port, std::uint32_t timeout_ms) - : m_host(host), m_port(port), m_timeout_ms(timeout_ms) {} - - //! dtor - ~sentinel_def(void) = default; - - public: - //! - //! \return sentinel host - //! - const std::string& - get_host(void) const { return m_host; } - - //! - //! \return sentinel port - //! - size_t - get_port(void) const { return m_port; } - - //! - //! \return timeout for sentinel - //! - std::uint32_t - get_timeout_ms(void) const { return m_timeout_ms; } - - //! - //! set connect timeout for sentinel in ms - //! \param timeout_ms new value - //! - void - set_timeout_ms(std::uint32_t timeout_ms) { m_timeout_ms = timeout_ms; } - - private: - //! - //! sentinel host - //! - std::string m_host; - - //! - //! sentinel port - //! - std::size_t m_port; - - //! - //! connect timeout config - //! - std::uint32_t m_timeout_ms; - }; - -public: - //! - //! \return sentinels - //! - const std::vector& get_sentinels(void) const; - - //! - //! \return sentinels (non-const version) - //! - std::vector& get_sentinels(void); - -private: - //! - //! redis connection receive handler, triggered whenever a reply has been read by the redis connection - //! - //! \param connection redis_connection instance - //! \param reply parsed reply - //! - void connection_receive_handler(network::redis_connection& connection, reply& reply); - - //! - //! redis_connection disconnection handler, triggered whenever a disconnection occurred - //! - //! \param connection redis_connection instance - //! - void connection_disconnect_handler(network::redis_connection& connection); - - //! - //! Call the user-defined disconnection handler - //! - void call_disconnect_handler(void); - - //! - //! reset the queue of pending callbacks - //! - void clear_callbacks(void); - - //! - //! try to commit the pending pipelined - //! if client is disconnected, will throw an exception and clear all pending callbacks (call clear_callbacks()) - //! - void try_commit(void); - -private: - //! - //! A pool of 1 or more sentinels we ask to determine which redis server is the master. - //! - std::vector m_sentinels; - - //! - //! tcp client for redis sentinel connection - //! - network::redis_connection m_client; - - //! - //! queue of callback to process - //! - std::queue m_callbacks; - - //! - //! user defined disconnection handler to be called on disconnection - //! - sentinel_disconnect_handler_t m_disconnect_handler; - - //! - //! callbacks thread safety - //! - std::mutex m_callbacks_mutex; - - //! - //! condvar for callbacks updates - //! - std::condition_variable m_sync_condvar; - - //! - //! number of callbacks currently being running - //! - std::atomic m_callbacks_running; -}; + /** + * custom ctor to specify custom tcp_client + * + * @param tcp_client tcp client to be used for network communications + * + */ + explicit sentinel(const std::shared_ptr &tcp_client); + + /** + * dtor + * + */ + ~sentinel(); + + /** + * copy ctor + * + */ + sentinel(const sentinel &) = delete; + + /** + * assignment operator + * + */ + sentinel &operator=(const sentinel &) = delete; + + public: + /** + * callback to be called whenever a reply has been received + * + */ + typedef std::function reply_callback_t; + + /** + * send the given command + * the command is actually pipelined and only buffered, so nothing is sent to the network + * please call commit() to flush the buffer + * + * @param sentinel_cmd command to be sent + * @param callback callback to be called when reply is received for this command + * @return current instance + * + */ + sentinel &send(const std::vector &sentinel_cmd, const reply_callback_t &callback = nullptr); + + /** + * commit pipelined transaction + * that is, send to the network all commands pipelined by calling send() + * + * @return current instance + * + */ + sentinel &commit(); + + /** + * same as commit(), but synchronous + * will block until all pending commands have been sent and that a reply has been received for each of them and all underlying callbacks completed + * + * @return current instance + * + */ + sentinel &sync_commit(); + + /** + * same as sync_commit, but with a timeout + * will simply block until it completes or timeout expires + * + * @return current instance + * + */ + template + sentinel & + sync_commit(const std::chrono::duration &timeout) { + try_commit(); + + std::unique_lock lock_callback(m_callbacks_mutex); + __CPP_REDIS_LOG(debug, "cpp_redis::sentinel waiting for callbacks to complete"); + if (!m_sync_condvar.wait_for(lock_callback, timeout, [=] { + return m_callbacks_running == 0 && m_callbacks.empty(); + })) { + __CPP_REDIS_LOG(debug, "cpp_redis::sentinel finished waiting for callback"); + } else { + __CPP_REDIS_LOG(debug, "cpp_redis::sentinel timed out waiting for callback"); + } + return *this; + } + + public: + /** + * add a sentinel definition. Required for connect() or get_master_addr_by_name() when autoconnect is enabled. + * + * @param host sentinel host + * @param port sentinel port + * @param timeout_ms maximum time to connect + * @return current instance + * + */ + sentinel &add_sentinel(const std::string &host, std::size_t port, std::uint32_t timeout_ms = 0); + + /** + * clear all existing sentinels. + * + */ + void clear_sentinels(); + + public: + /** + * disconnect from redis server + * + * @param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. + * + */ + void disconnect(bool wait_for_removal = false); + + /** + * @return whether we are connected to the redis server or not + * + */ + bool is_connected(); + + /** + * handlers called whenever disconnection occurred + * function takes the sentinel current instance as parameter + * + */ + typedef std::function sentinel_disconnect_handler_t; + + /** + * Connect to 1st active sentinel we find. Requires add_sentinel() to be called first + * will use timeout set for each added sentinel independently + * + * @param disconnect_handler handler to be called whenever disconnection occurs + * + */ + void connect_sentinel(const sentinel_disconnect_handler_t &disconnect_handler = nullptr); + + /** + * Connect to named sentinel + * + * @param host host to be connected to + * @param port port to be connected to + * @param timeout_ms maximum time to connect + * @param disconnect_handler handler to be called whenever disconnection occurs + * + */ + void connect( + const std::string &host, + std::size_t port, + const sentinel_disconnect_handler_t &disconnect_handler = nullptr, + std::uint32_t timeout_ms = 0); + + /** + * Used to find the current redis master by asking one or more sentinels. Use high availability. + * Handles connect() and disconnect() automatically when autoconnect=true + * This method is synchronous. No need to call sync_commit() or process a reply callback. + * Call add_sentinel() before using when autoconnect==true + * + * @param name sentinel name + * @param host sentinel host + * @param port sentinel port + * @param autoconnect autoconnect we loop through and connect/disconnect as necessary to sentinels that were added using add_sentinel(). + * Otherwise we rely on the call to connect to a sentinel before calling this method. + * @return true if a master was found and fills in host and port output parameters, false otherwise + */ + bool get_master_addr_by_name( + const std::string &name, + std::string &host, + std::size_t &port, + bool autoconnect = true); + + public: + sentinel &ckquorum(const std::string &name, const reply_callback_t &reply_callback = nullptr); + + sentinel &failover(const std::string &name, const reply_callback_t &reply_callback = nullptr); + + sentinel &flushconfig(const reply_callback_t &reply_callback = nullptr); + + sentinel &master(const std::string &name, const reply_callback_t &reply_callback = nullptr); + + sentinel &masters(const reply_callback_t &reply_callback = nullptr); + + sentinel &monitor(const std::string &name, const std::string &ip, std::size_t port, std::size_t quorum, + const reply_callback_t &reply_callback = nullptr); + + sentinel &ping(const reply_callback_t &reply_callback = nullptr); + + sentinel &remove(const std::string &name, const reply_callback_t &reply_callback = nullptr); + + sentinel &reset(const std::string &pattern, const reply_callback_t &reply_callback = nullptr); + + sentinel &sentinels(const std::string &name, const reply_callback_t &reply_callback = nullptr); + + sentinel &set(const std::string &name, const std::string &option, const std::string &value, + const reply_callback_t &reply_callback = nullptr); + + sentinel &slaves(const std::string &name, const reply_callback_t &reply_callback = nullptr); + + public: + /** + * store informations related to a sentinel + * typically, host, port and connection timeout + * + */ + class sentinel_def { + public: + /** + * ctor + * + */ + sentinel_def(std::string host, std::size_t port, std::uint32_t timeout_ms) + : m_host(std::move(host)), m_port(port), m_timeout_ms(timeout_ms) {} + + /** + * dtor + * + */ + ~sentinel_def() = default; + + public: + /** + * @return sentinel host + * + */ + const std::string & + get_host() const { return m_host; } + + /** + * @return sentinel port + * + */ + size_t + get_port() const { return m_port; } + + /** + * @return timeout for sentinel + * + */ + std::uint32_t + get_timeout_ms() const { return m_timeout_ms; } + + /** + * set connect timeout for sentinel in ms + * @param timeout_ms new value + * + */ + void + set_timeout_ms(std::uint32_t timeout_ms) { m_timeout_ms = timeout_ms; } + + private: + /** + * sentinel host + * + */ + std::string m_host; + + /** + * sentinel port + * + */ + std::size_t m_port; + + /** + * connect timeout config + * + */ + std::uint32_t m_timeout_ms; + }; + + public: + /** + * @return sentinels + * + */ + const std::vector &get_sentinels() const; + + /** + * @return sentinels (non-const version) + * + */ + std::vector &get_sentinels(); + + private: + /** + * redis connection receive handler, triggered whenever a reply has been read by the redis connection + * + * @param connection redis_connection instance + * @param reply parsed reply + * + */ + void connection_receive_handler(network::redis_connection &connection, reply &reply); + + /** + * redis_connection disconnection handler, triggered whenever a disconnection occurred + * + * @param connection redis_connection instance + * + */ + void connection_disconnect_handler(network::redis_connection &connection); + + /** + * Call the user-defined disconnection handler + * + */ + void call_disconnect_handler(); + +/** + * reset the queue of pending callbacks + * + */ + void clear_callbacks(); + +/** + * try to commit the pending pipelined + * if client is disconnected, will throw an exception and clear all pending callbacks (call clear_callbacks()) + * + */ + void try_commit(); + + private: + /** + * A pool of 1 or more sentinels we ask to determine which redis server is the master. + * + */ + std::vector m_sentinels; + + /** + * tcp client for redis sentinel connection + * + */ + network::redis_connection m_client; + + /** + * queue of callback to process + * + */ + std::queue m_callbacks; + + /** + * user defined disconnection handler to be called on disconnection + * + */ + sentinel_disconnect_handler_t m_disconnect_handler; + + /** + * callbacks thread safety + * + */ + std::mutex m_callbacks_mutex; + + /** + * condvar for callbacks updates + * + */ + std::condition_variable m_sync_condvar; + + /** + * number of callbacks currently being running + * + */ + std::atomic m_callbacks_running; + }; } // namespace cpp_redis diff --git a/includes/cpp_redis/core/subscriber.hpp b/includes/cpp_redis/core/subscriber.hpp index eace52e6..640ba7fd 100644 --- a/includes/cpp_redis/core/subscriber.hpp +++ b/includes/cpp_redis/core/subscriber.hpp @@ -35,45 +35,62 @@ namespace cpp_redis { -//! -//! The cpp_redis::subscriber is meant to be used for PUB/SUB communication with the Redis server. -//! Please do not use cpp_redis::client to subscribe to some Redis channels as: -//! * the behavior is undefined -//! * cpp_redis::client is not meant for that -//! +/** + * The cpp_redis::subscriber is meant to be used for PUB/SUB communication with the Redis server. + * Please do not use cpp_redis::client to subscribe to some Redis channels as: + * * the behavior is undefined + * * cpp_redis::client is not meant for that + * + */ class subscriber { public: #ifndef __CPP_REDIS_USE_CUSTOM_TCP_CLIENT - //! ctor +/** + * ctor + * + */ subscriber(); #endif /* __CPP_REDIS_USE_CUSTOM_TCP_CLIENT */ - //! - //! custom ctor to specify custom tcp_client - //! - //! \param tcp_client tcp client to be used for network communications - //! +/** + * custom ctor to specify custom tcp_client + * + * @param tcp_client tcp client to be used for network communications + * + */ explicit subscriber(const std::shared_ptr &tcp_client); - //! dtor +/** + * dtor + * + */ ~subscriber(); - //! copy ctor +/** + * copy ctor + * + */ subscriber(const subscriber &) = delete; - //! assignment operator +/** + * assignment operator + * + */ subscriber &operator=(const subscriber &) = delete; public: - //! \brief Connect to redis server - //! \param host host to be connected to - //! \param port port to be connected to - //! \param connect_callback connect handler to be called on connect events (may be null) - //! \param timeout_ms maximum time to connect - //! \param max_reconnects maximum attempts of reconnection if connection dropped - //! \param reconnect_interval_ms time between two attempts of reconnection +/** + * @brief Connect to redis server + * @param host host to be connected to + * @param port port to be connected to + * @param connect_callback connect handler to be called on connect events (may be null) + * @param timeout_ms maximum time to connect + * @param max_reconnects maximum attempts of reconnection if connection dropped + * @param reconnect_interval_ms time between two attempts of reconnection + * + */ void connect( const std::string &host = "127.0.0.1", std::size_t port = 6379, @@ -82,12 +99,15 @@ namespace cpp_redis { std::int32_t max_reconnects = 0, std::uint32_t reconnect_interval_ms = 0); - //! \brief Connect to redis server - //! \param name sentinel name - //! \param connect_callback connect handler to be called on connect events (may be null) - //! \param timeout_ms maximum time to connect - //! \param max_reconnects maximum attempts of reconnection if connection dropped - //! \param reconnect_interval_ms time between two attempts of reconnection +/** + * @brief Connect to redis server + * @param name sentinel name + * @param connect_callback connect handler to be called on connect events (may be null) + * @param timeout_ms maximum time to connect + * @param max_reconnects maximum attempts of reconnection if connection dropped + * @param reconnect_interval_ms time between two attempts of reconnection + * + */ void connect( const std::string &name, const connect_callback_t &connect_callback = nullptr, @@ -95,319 +115,380 @@ namespace cpp_redis { std::int32_t max_reconnects = 0, std::uint32_t reconnect_interval_ms = 0); - //! \brief determines client connectivity - //! \return whether we are connected to the redis server +/** + * @brief determines client connectivity + * @return whether we are connected to the redis server + * + */ bool is_connected() const; - //! \brief disconnect from redis server - //! \param wait_for_removal when set to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. +/** + * @brief disconnect from redis server + * @param wait_for_removal when set to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. + * + */ void disconnect(bool wait_for_removal = false); - //! \brief determines if reconnect is in progress - //! \return whether an attempt to reconnect is in progress +/** + * @brief determines if reconnect is in progress + * @return whether an attempt to reconnect is in progress + * + */ bool is_reconnecting() const; - //! \brief stop any reconnect in progress +/** + * @brief stop any reconnect in progress + * + */ void cancel_reconnect(); public: - //! \brief reply callback called whenever a reply is received, takes as parameter the received reply +/** + * @brief reply callback called whenever a reply is received, takes as parameter the received reply + * + */ typedef std::function reply_callback_t; - //! \brief ability to authenticate on the redis server if necessary - //! this method should not be called repeatedly as the storage of reply_callback is NOT thread safe (only one reply callback is stored for the subscriber client) - //! calling repeatedly auth() is undefined concerning the execution of the associated callbacks - //! \param password password to be used for authentication - //! \param reply_callback callback to be called on auth completion (nullable) - //! \return current instance +/** + * @brief ability to authenticate on the redis server if necessary + * this method should not be called repeatedly as the storage of reply_callback is NOT thread safe (only one reply callback is stored for the subscriber client) + * calling repeatedly auth() is undefined concerning the execution of the associated callbacks + * @param password password to be used for authentication + * @param reply_callback callback to be called on auth completion (nullable) + * @return current instance + * + */ subscriber &auth(const std::string &password, const reply_callback_t &reply_callback = nullptr); - //! - //! subscribe callback, called whenever a new message is published on a subscribed channel - //! takes as parameter the channel and the message - //! +/** + * subscribe callback, called whenever a new message is published on a subscribed channel + * takes as parameter the channel and the message + * + */ typedef std::function subscribe_callback_t; - //! - //! Subscribes to the given channel and: - //! * calls acknowledgement_callback once the server has acknowledged about the subscription. - //! * calls subscribe_callback each time a message is published on this channel. - //! The command is not effectively sent immediately but stored in an internal buffer until commit() is called. - //! - //! \param channel channel to subscribe - //! \param callback callback to be called whenever a message is received for this channel - //! \param acknowledgement_callback callback to be called on subscription completion (nullable) - //! \return current instance - //! +/** + * Subscribes to the given channel and: + * * calls acknowledgement_callback once the server has acknowledged about the subscription. + * * calls subscribe_callback each time a message is published on this channel. + * The command is not effectively sent immediately but stored in an internal buffer until commit() is called. + * + * @param channel channel to subscribe + * @param callback callback to be called whenever a message is received for this channel + * @param acknowledgement_callback callback to be called on subscription completion (nullable) + * @return current instance + */ +//! subscriber &subscribe(const std::string &channel, const subscribe_callback_t &callback, const acknowledgement_callback_t &acknowledgement_callback = nullptr); - //! - //! PSubscribes to the given channel and: - //! * calls acknowledgement_callback once the server has acknowledged about the subscription. - //! * calls subscribe_callback each time a message is published on this channel. - //! The command is not effectively sent immediately but stored in an internal buffer until commit() is called. - //! - //! \param pattern pattern to psubscribe - //! \param callback callback to be called whenever a message is received for this pattern - //! \param acknowledgement_callback callback to be called on subscription completion (nullable) - //! \return current instance - //! +/** + * PSubscribes to the given channel and: + * * calls acknowledgement_callback once the server has acknowledged about the subscription. + * * calls subscribe_callback each time a message is published on this channel. + * The command is not effectively sent immediately but stored in an internal buffer until commit() is called. + * + * @param pattern pattern to psubscribe + * @param callback callback to be called whenever a message is received for this pattern + * @param acknowledgement_callback callback to be called on subscription completion (nullable) + * @return current instance + */ +//! subscriber &psubscribe(const std::string &pattern, const subscribe_callback_t &callback, const acknowledgement_callback_t &acknowledgement_callback = nullptr); - //! - //! unsubscribe from the given channel - //! The command is not effectively sent immediately, but stored inside an internal buffer until commit() is called. - //! - //! \param channel channel to unsubscribe from - //! \return current instance - //! +/** + * unsubscribe from the given channel + * The command is not effectively sent immediately, but stored inside an internal buffer until commit() is called. + * + * @param channel channel to unsubscribe from + * @return current instance + * + */ subscriber &unsubscribe(const std::string &channel); - //! - //! punsubscribe from the given pattern - //! The command is not effectively sent immediately, but stored inside an internal buffer until commit() is called. - //! - //! \param pattern pattern to punsubscribe from - //! \return current instance - //! +/** + * punsubscribe from the given pattern + * The command is not effectively sent immediately, but stored inside an internal buffer until commit() is called. + * + * @param pattern pattern to punsubscribe from + * @return current instance + * + */ subscriber &punsubscribe(const std::string &pattern); - //! - //! commit pipelined transaction - //! that is, send to the network all commands pipelined by calling send() / subscribe() / ... - //! - //! \return current instance - //! +/** + * commit pipelined transaction + * that is, send to the network all commands pipelined by calling send() / subscribe() / ... + * + * @return current instance + * + */ subscriber &commit(); public: - //! - //! add a sentinel definition. Required for connect() or get_master_addr_by_name() when autoconnect is enabled. - //! - //! \param host sentinel host - //! \param port sentinel port - //! \param timeout_ms maximum time to connect - //! +/** + * add a sentinel definition. Required for connect() or get_master_addr_by_name() when autoconnect is enabled. + * + * @param host sentinel host + * @param port sentinel port + * @param timeout_ms maximum time to connect + * + */ void add_sentinel(const std::string &host, std::size_t port, std::uint32_t timeout_ms = 0); - //! - //! retrieve sentinel for current client - //! - //! \return sentinel associated to current client - //! +/** + * retrieve sentinel for current client + * + * @return sentinel associated to current client + * + */ const sentinel &get_sentinel() const; - //! - //! retrieve sentinel for current client - //! non-const version - //! - //! \return sentinel associated to current client - //! +/** + * retrieve sentinel for current client + * non-const version + * + * @return sentinel associated to current client + * + */ sentinel &get_sentinel(); - //! - //! clear all existing sentinels. - //! +/** + * clear all existing sentinels. + * + */ void clear_sentinels(); private: - //! - //! struct to hold callbacks (sub and ack) for a given channel or pattern - //! +/** + * struct to hold callbacks (sub and ack) for a given channel or pattern + * + */ struct callback_holder { subscribe_callback_t subscribe_callback; acknowledgement_callback_t acknowledgement_callback; }; private: - //! - //! redis connection receive handler, triggered whenever a reply has been read by the redis connection - //! - //! \param connection redis_connection instance - //! \param reply parsed reply - //! +/** + * redis connection receive handler, triggered whenever a reply has been read by the redis connection + * + * @param connection redis_connection instance + * @param reply parsed reply + * + */ void connection_receive_handler(network::redis_connection &connection, reply &reply); - //! - //! redis_connection disconnection handler, triggered whenever a disconnection occurred - //! - //! \param connection redis_connection instance - //! +/** + * redis_connection disconnection handler, triggered whenever a disconnection occurred + * + * @param connection redis_connection instance + * + */ void connection_disconnection_handler(network::redis_connection &connection); - //! - //! trigger the ack callback for matching channel/pattern - //! check if reply is valid - //! - //! \param reply received reply - //! +/** + * trigger the ack callback for matching channel/pattern + * check if reply is valid + * + * @param reply received reply + * + */ void handle_acknowledgement_reply(const std::vector &reply); - //! - //! trigger the sub callback for all matching channels/patterns - //! check if reply is valid - //! - //! \param reply received reply - //! +/** + * trigger the sub callback for all matching channels/patterns + * check if reply is valid + * + * @param reply received reply + * + */ void handle_subscribe_reply(const std::vector &reply); - //! - //! trigger the sub callback for all matching channels/patterns - //! check if reply is valid - //! - //! \param reply received reply - //! +/** + * trigger the sub callback for all matching channels/patterns + * check if reply is valid + * + * @param reply received reply + * + */ void handle_psubscribe_reply(const std::vector &reply); - //! - //! find channel or pattern that is associated to the reply and call its ack callback - //! - //! \param channel channel or pattern that caused the issuance of this reply - //! \param channels list of channels or patterns to be searched for the received channel - //! \param channels_mtx channels or patterns mtx to be locked for race condition - //! \param nb_chans redis server ack reply - //! +/** + * find channel or pattern that is associated to the reply and call its ack callback + * + * @param channel channel or pattern that caused the issuance of this reply + * @param channels list of channels or patterns to be searched for the received channel + * @param channels_mtx channels or patterns mtx to be locked for race condition + * @param nb_chans redis server ack reply + * + */ void call_acknowledgement_callback(const std::string &channel, const std::map &channels, std::mutex &channels_mtx, int64_t nb_chans); private: - //! - //! reconnect to the previously connected host - //! automatically re authenticate and resubscribe to subscribed channel in case of success - //! +/** + * reconnect to the previously connected host + * automatically re authenticate and resubscribe to subscribed channel in case of success + * + */ void reconnect(); - //! - //! re authenticate to redis server based on previously used password - //! +/** + * re authenticate to redis server based on previously used password + * + */ void re_auth(); - //! - //! resubscribe (sub and psub) to previously subscribed channels/patterns - //! +/** + * resubscribe (sub and psub) to previously subscribed channels/patterns + * + */ void re_subscribe(); - //! - //! \return whether a reconnection attempt should be performed - //! +/** + * @return whether a reconnection attempt should be performed + * + */ bool should_reconnect() const; - //! - //! sleep between two reconnect attempts if necessary - //! +/** + * sleep between two reconnect attempts if necessary + * + */ void sleep_before_next_reconnect_attempt(); - //! - //! clear all subscriptions (dirty way, no unsub/punsub commands send: mostly used for cleaning in disconnection condition) - //! +/** + * clear all subscriptions (dirty way, no unsub/punsub commands send: mostly used for cleaning in disconnection condition) + * + */ void clear_subscriptions(); private: - //! - //! unprotected sub - //! same as subscribe, but without any mutex lock - //! - //! \param channel channel to subscribe - //! \param callback callback to be called whenever a message is received for this channel - //! \param acknowledgement_callback callback to be called on subscription completion (nullable) - //! +/** + * unprotected sub + * same as subscribe, but without any mutex lock + * + * @param channel channel to subscribe + * @param callback callback to be called whenever a message is received for this channel + * @param acknowledgement_callback callback to be called on subscription completion (nullable) + * + */ void unprotected_subscribe(const std::string &channel, const subscribe_callback_t &callback, const acknowledgement_callback_t &acknowledgement_callback); - //! - //! unprotected psub - //! same as psubscribe, but without any mutex lock - //! - //! \param pattern pattern to psubscribe - //! \param callback callback to be called whenever a message is received for this pattern - //! \param acknowledgement_callback callback to be called on subscription completion (nullable) - //! +/** + * unprotected psub + * same as psubscribe, but without any mutex lock + * + * @param pattern pattern to psubscribe + * @param callback callback to be called whenever a message is received for this pattern + * @param acknowledgement_callback callback to be called on subscription completion (nullable) + * + */ void unprotected_psubscribe(const std::string &pattern, const subscribe_callback_t &callback, const acknowledgement_callback_t &acknowledgement_callback); private: - //! - //! server we are connected to - //! +/** + * server we are connected to + * + */ std::string m_redis_server; - //! - //! port we are connected to - //! +/** + * port we are connected to + * + */ std::size_t m_redis_port = 0; - //! - //! master name (if we are using sentinel) we are connected to - //! +/** + * master name (if we are using sentinel) we are connected to + * + */ std::string m_master_name; - //! - //! password used to authenticate - //! +/** + * password used to authenticate + * + */ std::string m_password; - //! - //! tcp client for redis connection - //! +/** + * tcp client for redis connection + * + */ network::redis_connection m_client; - //! - //! redis sentinel - //! +/** + * redis sentinel + * + */ cpp_redis::sentinel m_sentinel; - //! - //! max time to connect - //! +/** + * max time to connect + * + */ std::uint32_t m_connect_timeout_ms = 0; - //! - //! max number of reconnection attempts - //! +/** + * max number of reconnection attempts + * + */ std::int32_t m_max_reconnects = 0; - //! - //! current number of attempts to reconnect - //! +/** + * current number of attempts to reconnect + * + */ std::int32_t m_current_reconnect_attempts = 0; - //! - //! time between two reconnection attempts - //! +/** + * time between two reconnection attempts + * + */ std::uint32_t m_reconnect_interval_ms = 0; - //! - //! reconnection status - //! +/** + * reconnection status + * + */ std::atomic_bool m_reconnecting; - //! - //! to force cancel reconnection - //! +/** + * to force cancel reconnection + * + */ std::atomic_bool m_cancel; - //! - //! subscribed channels and their associated channels - //! +/** + * subscribed channels and their associated channels + * + */ std::map m_subscribed_channels; - //! - //! psubscribed channels and their associated channels - //! +/** + * psubscribed channels and their associated channels + * + */ std::map m_psubscribed_channels; - //! - //! connect handler - //! +/** + * connect handler + * + */ connect_callback_t m_connect_callback; - //! - //! sub chans thread safety - //! +/** + * sub chans thread safety + * + */ std::mutex m_psubscribed_channels_mutex; - //! - //! psub chans thread safety - //! +/** + * psub chans thread safety + * + */ std::mutex m_subscribed_channels_mutex; - //! - //! auth reply callback - //! +/** + * auth reply callback + * + */ reply_callback_t m_auth_reply_callback; }; diff --git a/includes/cpp_redis/core/types.hpp b/includes/cpp_redis/core/types.hpp index 51ca7e00..46a6e420 100644 --- a/includes/cpp_redis/core/types.hpp +++ b/includes/cpp_redis/core/types.hpp @@ -34,7 +34,10 @@ namespace cpp_redis { typedef std::int64_t ms; - //! \brief first array is the session name, second is ids +/** + * @brief first array is the session name, second is ids + * + */ typedef std::pair, std::vector> streams_t; /** @@ -117,31 +120,45 @@ namespace cpp_redis { class xstream_reply : public std::vector { public: - explicit xstream_reply(const reply &data); + explicit xstream_reply(const reply_t &data); friend std::ostream &operator<<(std::ostream &os, const xstream_reply &xs); + + bool is_null() const { + if (empty()) + return true; + for (auto &v : *this) { + if (v.Messages.empty()) + return true; + } + return false; + } }; + typedef xstream_reply xstream_reply_t; + /** * @brief Callbacks */ - //! - //! acknowledgment callback called whenever a subscribe completes - //! takes as parameter the int returned by the redis server (usually the number of channels you are subscribed to) - //! - typedef std::function acknowledgement_callback_t; - - //! - //! high availability (re)connection states - //! * dropped: connection has dropped - //! * start: attempt of connection has started - //! * sleeping: sleep between two attempts - //! * ok: connected - //! * failed: failed to connect - //! * lookup failed: failed to retrieve master sentinel - //! * stopped: stop to try to reconnect - //! +/** + * acknowledgment callback called whenever a subscribe completes + * takes as parameter the int returned by the redis server (usually the number of channels you are subscribed to) + * + */ + typedef std::function acknowledgement_callback_t; + +/** + * high availability (re)connection states + * * dropped: connection has dropped + * * start: attempt of connection has started + * * sleeping: sleep between two attempts + * * ok: connected + * * failed: failed to connect + * * lookup failed: failed to retrieve master sentinel + * * stopped: stop to try to reconnect + * + */ enum class connect_state { dropped, start, @@ -152,9 +169,10 @@ namespace cpp_redis { stopped }; - //! - //! connect handler, called whenever a new connection even occurred - //! +/** + * connect handler, called whenever a new connection even occurred + * + */ typedef std::function connect_callback_t; typedef std::function message_callback_t; diff --git a/includes/cpp_redis/cpp_redis b/includes/cpp_redis/cpp_redis index 602f073e..60438ae9 100644 --- a/includes/cpp_redis/cpp_redis +++ b/includes/cpp_redis/cpp_redis @@ -20,7 +20,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#pragma once +#ifndef __CPP_REDIS_ +#define __CPP_REDIS_ #ifdef _WIN32 #pragma comment( lib, "ws2_32.lib") @@ -34,6 +35,8 @@ #include #include +#endif + #ifndef __CPP_REDIS_USE_CUSTOM_TCP_CLIENT #include #endif /* __CPP_REDIS_USE_CUSTOM_TCP_CLIENT */ diff --git a/includes/cpp_redis/helpers/generate_rand.hpp b/includes/cpp_redis/helpers/generate_rand.hpp index c3c083c9..d50fa2d7 100644 --- a/includes/cpp_redis/helpers/generate_rand.hpp +++ b/includes/cpp_redis/helpers/generate_rand.hpp @@ -1,28 +1,27 @@ -/* - * - * Created by nick on 11/22/18. - * - * Copyright(c) 2018 Iris. All rights reserved. - * - * Use and copying of this software and preparation of derivative - * works based upon this software are not permitted. Any distribution - * of this software or derivative works must comply with all applicable - * Canadian export control laws. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL IRIS OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ +// +// Created by nick on 11/22/18. +// +// The MIT License (MIT) +// +// Copyright (c) 2015-2017 Simon Ninon +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. #ifndef CPP_REDIS_GENERATE_RAND_HPP #define CPP_REDIS_GENERATE_RAND_HPP diff --git a/includes/cpp_redis/helpers/variadic_template.hpp b/includes/cpp_redis/helpers/variadic_template.hpp index ca4cb952..4cfbdc83 100644 --- a/includes/cpp_redis/helpers/variadic_template.hpp +++ b/includes/cpp_redis/helpers/variadic_template.hpp @@ -27,87 +27,101 @@ namespace cpp_redis { namespace helpers { -//! -//! type traits to return last element of a variadic list -//! +/** + * type traits to return last element of a variadic list + * + */ template struct back { - //! - //! last type of variadic list - //! +/** + * last type of variadic list + * + */ using type = typename back::type; }; -//! -//! type traits to return last element of a variadic list -//! +/** + * type traits to return last element of a variadic list + * + */ template struct back { - //! - //! templated type - //! +/** + * templated type + * + */ using type = T; }; -//! -//! type traits to return front element of a variadic list -//! +/** + * type traits to return front element of a variadic list + * + */ template struct front { - //! - //! front type of variadic list - //! +/** + * front type of variadic list + * + */ using type = T; }; -//! -//! type traits to check if type is present in variadic list -//! +/** + * type traits to check if type is present in variadic list + * + */ template struct is_type_present { - //! - //! true if T1 is present in remaining types of variadic list - //! false otherwise - //! +/** + * true if T1 is present in remaining types of variadic list + * false otherwise + * + */ static constexpr bool value = std::is_same::value ? true : is_type_present::value; }; -//! -//! type traits to check if type is present in variadic list -//! +/** + * type traits to check if type is present in variadic list + * + */ template struct is_type_present { - //! - //! true if T1 and T2 are the same - //! false otherwise - //! +/** + * true if T1 and T2 are the same + * false otherwise + * + */ static constexpr bool value = std::is_same::value; }; -//! -//! type traits to check if type is not present in variadic list -//! +/** + * type traits to check if type is not present in variadic list + * + */ template struct is_different_types { - //! - //! true if T is not in remaining types of variadic list - //! false otherwise - //! +/** + * true if T is not in remaining types of variadic list + * false otherwise + * + */ static constexpr bool value = is_type_present::value ? false : is_different_types::value; }; -//! -//! type traits to check if type is not present in variadic list -//! +/** + * type traits to check if type is not present in variadic list + * + */ template struct is_different_types { - //! - //! true - //! +/** + * true + * + */ static constexpr bool value = true; }; diff --git a/includes/cpp_redis/impl/client.ipp b/includes/cpp_redis/impl/client.ipp index e1d9cde2..4fb07635 100644 --- a/includes/cpp_redis/impl/client.ipp +++ b/includes/cpp_redis/impl/client.ipp @@ -94,7 +94,7 @@ client::client_kill(const std::string& host, int port, const T& arg, const Ts&.. static_assert(helpers::is_different_types::value, "Should only have one distinct value per filter type"); std::vector redis_cmd({"CLIENT", "KILL"}); - //! If we have other type than lambda, then it's a filter +//! If we have other type than lambda, then it's a filter if (!std::is_class::value) { redis_cmd.emplace_back("ADDR"); } @@ -120,8 +120,8 @@ client::client_kill(const char* host, int port, const Ts&... args) { template std::future client::client_kill_future(const T arg, const Ts... args) { - //! gcc 4.8 doesn't handle variadic template capture arguments (appears in 4.9) - //! so std::bind should capture all arguments because of the compiler. +//! gcc 4.8 doesn't handle variadic template capture arguments (appears in 4.9) +//! so std::bind should capture all arguments because of the compiler. return exec_cmd(std::bind([this](T arg, Ts... args, const reply_callback_t& cb) -> client& { return client_kill(arg, args..., cb); }, diff --git a/includes/cpp_redis/impl/types.hpp b/includes/cpp_redis/impl/types.hpp index 07600229..1fcd9100 100644 --- a/includes/cpp_redis/impl/types.hpp +++ b/includes/cpp_redis/impl/types.hpp @@ -42,14 +42,16 @@ namespace cpp_redis { public: inline serializer_type() {} - //! - //! \return the underlying string - //! +/** + * @return the underlying string + * + */ virtual const std::string &as_string() const = 0; - //! - //! \return the underlying integer - //! +/** + * @return the underlying integer + * + */ virtual optional try_get_int() const = 0; protected: @@ -127,6 +129,16 @@ namespace cpp_redis { inline const std::multimap &get_values() const override { return m_values; }; + + inline std::multimap &get_str_values() const { + std::multimap ret; + for (auto &v : m_values) { + std::stringstream s; + s << v.second; + ret.insert({v.first, s.str()}); + } + return ret; + }; }; } diff --git a/includes/cpp_redis/misc/dispatch_queue.hpp b/includes/cpp_redis/misc/dispatch_queue.hpp index 09ca4d5d..db2008eb 100644 --- a/includes/cpp_redis/misc/dispatch_queue.hpp +++ b/includes/cpp_redis/misc/dispatch_queue.hpp @@ -1,28 +1,27 @@ -/* - * - * Created by nick on 11/22/18. - * - * Copyright(c) 2018 Iris. All rights reserved. - * - * Use and copying of this software and preparation of derivative - * works based upon this software are not permitted. Any distribution - * of this software or derivative works must comply with all applicable - * Canadian export control laws. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL IRIS OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ +// The MIT License (MIT) +// +// Copyright (c) 11/27/18 nick. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE.#ifndef CPP_REDIS_CONVERT_HPP +// +// Code modified from https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/dispatch.cpp +// #ifndef CPP_REDIS_DISPATCH_QUEUE_HPP #define CPP_REDIS_DISPATCH_QUEUE_HPP @@ -40,7 +39,11 @@ #include namespace cpp_redis { - typedef std::function dispatch_callback_t; + typedef std::multimap consumer_response_t; + + typedef std::function dispatch_callback_t; + + typedef std::function notify_callback_t; typedef struct dispatch_callback_collection { dispatch_callback_t callback; @@ -50,7 +53,7 @@ namespace cpp_redis { class dispatch_queue { public: - explicit dispatch_queue(std::string name, size_t thread_cnt = 1); + explicit dispatch_queue(std::string name, const notify_callback_t ¬ify_callback, size_t thread_cnt = 1); ~dispatch_queue(); // dispatch and copy @@ -75,10 +78,13 @@ namespace cpp_redis { std::condition_variable m_cv; bool m_quit = false; + notify_callback_t notify_handler; + void dispatch_thread_handler(); }; typedef dispatch_queue dispatch_queue_t; + typedef std::unique_ptr dispatch_queue_ptr_t; } diff --git a/includes/cpp_redis/misc/error.hpp b/includes/cpp_redis/misc/error.hpp index 132d5181..d40ff643 100644 --- a/includes/cpp_redis/misc/error.hpp +++ b/includes/cpp_redis/misc/error.hpp @@ -27,20 +27,27 @@ namespace cpp_redis { -//! -//! specialized runtime_error used for cpp_redis error -//! +/** + * specialized runtime_error used for cpp_redis error + * + */ class redis_error : public std::runtime_error { public: using std::runtime_error::runtime_error; using std::runtime_error::what; - //! ctor (string) +/** + * ctor (string) + * + */ explicit redis_error(const std::string& err) : std::runtime_error(err.c_str()) { } - //! ctor(char*) +/** + * ctor(char*) + * + */ explicit redis_error(const char* err) : std::runtime_error(err) { } diff --git a/includes/cpp_redis/misc/logger.hpp b/includes/cpp_redis/misc/logger.hpp index 9a707048..873a2d25 100644 --- a/includes/cpp_redis/misc/logger.hpp +++ b/includes/cpp_redis/misc/logger.hpp @@ -28,68 +28,87 @@ namespace cpp_redis { -//! -//! logger_iface -//! should be inherited by any class intended to be used for logging -//! +/** + * logger_iface + * should be inherited by any class intended to be used for logging + * + */ class logger_iface { public: - //! ctor - logger_iface(void) = default; - //! dtor - virtual ~logger_iface(void) = default; +/** + * ctor + * + */ + logger_iface() = default; +/** + * dtor + * + */ + virtual ~logger_iface() = default; - //! copy ctor +/** + * copy ctor + * + */ logger_iface(const logger_iface&) = default; - //! assignment operator +/** + * assignment operator + * + */ logger_iface& operator=(const logger_iface&) = default; public: - //! - //! debug logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! +/** + * debug logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ virtual void debug(const std::string& msg, const std::string& file, std::size_t line) = 0; - //! - //! info logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! +/** + * info logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ virtual void info(const std::string& msg, const std::string& file, std::size_t line) = 0; - //! - //! warn logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! +/** + * warn logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ virtual void warn(const std::string& msg, const std::string& file, std::size_t line) = 0; - //! - //! error logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! +/** + * error logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ virtual void error(const std::string& msg, const std::string& file, std::size_t line) = 0; }; -//! -//! default logger class provided by the library -//! +/** + * default logger class provided by the library + * + */ class logger : public logger_iface { public: - //! - //! log level - //! +/** + * log level + * + */ enum class log_level { error = 0, warn = 1, @@ -98,114 +117,138 @@ class logger : public logger_iface { }; public: - //! ctor - logger(log_level level = log_level::info); - //! dtor - ~logger(void) = default; +/** + * ctor + * + */ + explicit logger(log_level level = log_level::info); +/** + * dtor + * + */ + ~logger() override = default; - //! copy ctor +/** + * copy ctor + * + */ logger(const logger&) = default; - //! assignment operator +/** + * assignment operator + * + */ logger& operator=(const logger&) = default; public: - //! - //! debug logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! - void debug(const std::string& msg, const std::string& file, std::size_t line); - - //! - //! info logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! - void info(const std::string& msg, const std::string& file, std::size_t line); - - //! - //! warn logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! - void warn(const std::string& msg, const std::string& file, std::size_t line); - - //! - //! error logging - //! - //! \param msg message to be logged - //! \param file file from which the message is coming - //! \param line line in the file of the message - //! - void error(const std::string& msg, const std::string& file, std::size_t line); +/** + * debug logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ + void debug(const std::string& msg, const std::string& file, std::size_t line) override; + +/** + * info logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ + void info(const std::string& msg, const std::string& file, std::size_t line) override; + +/** + * warn logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ + void warn(const std::string& msg, const std::string& file, std::size_t line) override; + +/** + * error logging + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ + void error(const std::string& msg, const std::string& file, std::size_t line) override; private: - //! - //! current log level in use - //! +/** + * current log level in use + * + */ log_level m_level; - //! - //! mutex used to serialize logs in multithreaded environment - //! +/** + * mutex used to serialize logs in multi-threaded environment + * + */ std::mutex m_mutex; }; -//! -//! variable containing the current logger -//! by default, not set (no logs) -//! +/** + * variable containing the current logger + * by default, not set (no logs) + * + */ extern std::unique_ptr active_logger; -//! -//! debug logging -//! convenience function used internally to call the logger -//! -//! \param msg message to be logged -//! \param file file from which the message is coming -//! \param line line in the file of the message -//! +/** + * debug logging + * convenience function used internally to call the logger + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ void debug(const std::string& msg, const std::string& file, std::size_t line); -//! -//! info logging -//! convenience function used internally to call the logger -//! -//! \param msg message to be logged -//! \param file file from which the message is coming -//! \param line line in the file of the message -//! +/** + * info logging + * convenience function used internally to call the logger + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ void info(const std::string& msg, const std::string& file, std::size_t line); -//! -//! warn logging -//! convenience function used internally to call the logger -//! -//! \param msg message to be logged -//! \param file file from which the message is coming -//! \param line line in the file of the message -//! +/** + * warn logging + * convenience function used internally to call the logger + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ void warn(const std::string& msg, const std::string& file, std::size_t line); -//! -//! error logging -//! convenience function used internally to call the logger -//! -//! \param msg message to be logged -//! \param file file from which the message is coming -//! \param line line in the file of the message -//! +/** + * error logging + * convenience function used internally to call the logger + * + * @param msg message to be logged + * @param file file from which the message is coming + * @param line line in the file of the message + * + */ void error(const std::string& msg, const std::string& file, std::size_t line); -//! -//! convenience macro to log with file and line information -//! +/** + * convenience macro to log with file and line information + * + */ #ifdef __CPP_REDIS_LOGGING_ENABLED #define __CPP_REDIS_LOG(level, msg) cpp_redis::level(msg, __FILE__, __LINE__); #else diff --git a/includes/cpp_redis/network/redis_connection.hpp b/includes/cpp_redis/network/redis_connection.hpp index 4cb7f1d1..9714aecb 100644 --- a/includes/cpp_redis/network/redis_connection.hpp +++ b/includes/cpp_redis/network/redis_connection.hpp @@ -39,54 +39,71 @@ namespace cpp_redis { namespace network { -//! -//! tcp connection wrapper handling redis protocol -//! +/** + * tcp connection wrapper handling redis protocol + * + */ class redis_connection { public: #ifndef __CPP_REDIS_USE_CUSTOM_TCP_CLIENT - //! ctor +/** + * ctor + * + */ redis_connection(); #endif /* __CPP_REDIS_USE_CUSTOM_TCP_CLIENT */ - //! - //! ctor allowing to specify custom tcp client (default ctor uses the default tacopie tcp client) - //! - //! \param tcp_client tcp client to be used for network communications - //! +/** + * ctor allowing to specify custom tcp client (default ctor uses the default tacopie tcp client) + * + * @param tcp_client tcp client to be used for network communications + * + */ explicit redis_connection(const std::shared_ptr &tcp_client); - //! dtor +/** + * dtor + * + */ ~redis_connection(); - //! copy ctor +/** + * copy ctor + * + */ redis_connection(const redis_connection &) = delete; - //! assignment operator +/** + * assignment operator + * + */ redis_connection &operator=(const redis_connection &) = delete; public: - //! - //! disconnection handler takes as parameter the instance of the redis_connection - //! +/** + * disconnection handler takes as parameter the instance of the redis_connection + * + */ typedef std::function disconnection_handler_t; - //! - //! reply handler takes as parameter the instance of the redis_connection and the built reply - //! +/** + * reply handler takes as parameter the instance of the redis_connection and the built reply + * + */ typedef std::function reply_callback_t; - //! - //! connect to the given host and port, and set both disconnection and reply callbacks - //! - //! \param host host to be connected to - //! \param port port to be connected to - //! \param disconnection_handler handler to be called in case of disconnection - //! \param reply_callback handler to be called once a reply is ready - //! \param timeout_ms max time to connect (in ms) - //! +/** + * connect to the given host and port, and set both disconnection and reply callbacks + * + * @param host host to be connected to + * @param port port to be connected to + * @param disconnection_handler handler to be called in case of disconnection + * @param reply_callback handler to be called once a reply is ready + * @param timeout_ms max time to connect (in ms) + * + */ void connect( const std::string &host = "127.0.0.1", std::size_t port = 6379, @@ -94,92 +111,106 @@ namespace cpp_redis { const reply_callback_t &reply_callback = nullptr, std::uint32_t timeout_ms = 0); - //! - //! disconnect from redis server - //! - //! \param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. - //! +/** + * disconnect from redis server + * + * @param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. + * + */ void disconnect(bool wait_for_removal = false); - //! - //! \return whether we are connected to the redis server or not - //! +/** + * @return whether we are connected to the redis server or not + * + */ bool is_connected() const; - //! - //! send the given command - //! the command is actually pipelined and only buffered, so nothing is sent to the network - //! please call commit() to flush the buffer - //! - //! \param redis_cmd command to be sent - //! \return current instance - //! +/** + * send the given command + * the command is actually pipelined and only buffered, so nothing is sent to the network + * please call commit() to flush the buffer + * + * @param redis_cmd command to be sent + * @return current instance + * + */ redis_connection &send(const std::vector &redis_cmd); - //! - //! commit pipelined transaction - //! that is, send to the network all commands pipelined by calling send() - //! - //! \return current instance - //! +/** + * commit pipelined transaction + * that is, send to the network all commands pipelined by calling send() + * + * @return current instance + * + */ redis_connection &commit(); private: - //! - //! tcp_client receive handler - //! called by the tcp_client whenever a read has completed - //! - //! \param result read result - //! +/** + * tcp_client receive handler + * called by the tcp_client whenever a read has completed + * + * @param result read result + * + */ void tcp_client_receive_handler(const tcp_client_iface::read_result &result); - //! - //! tcp_client disconnection handler - //! called by the tcp_client whenever a disconnection occurred - //! +/** + * tcp_client disconnection handler + * called by the tcp_client whenever a disconnection occurred + * + */ void tcp_client_disconnection_handler(); - //! - //! transform a user command to a redis command using the redis protocol format - //! for example, transform {"GET", "HELLO"} to something like "*2\r\n+GET\r\n+HELLO\r\n" - //! +/** + * transform a user command to a redis command using the redis protocol format + * for example, transform {"GET", "HELLO"} to something like "*2\r\n+GET\r\n+HELLO\r\n" + * + */ std::string build_command(const std::vector &redis_cmd); private: - //! - //! simply call the disconnection handler (does nothing if disconnection handler is set to null) - //! +/** + * simply call the disconnection handler (does nothing if disconnection handler is set to null) + * + */ void call_disconnection_handler(); private: - //! - //! tcp client for redis connection - //! +/** + * tcp client for redis connection + * + */ std::shared_ptr m_client; - //! - //! reply callback called whenever a reply has been read - //! +/** + * reply callback called whenever a reply has been read + * + */ reply_callback_t m_reply_callback; - //! - //! disconnection handler whenever a disconnection occurred - //! +/** + * disconnection handler whenever a disconnection occurred + * + */ disconnection_handler_t m_disconnection_handler; - //! - //! reply builder used to build replies - //! +/** + * reply builder used to build replies + * + */ builders::reply_builder m_builder; - //! - //! internal buffer used for pipelining (commands are buffered here and flushed to the tcp client when commit is called) - //! +/** + * internal buffer used for pipelining (commands are buffered here and flushed to the tcp client when commit is called) + * + */ std::string m_buffer; - //! - //! protect internal buffer against race conditions - //! +/** + * protect internal buffer against race conditions + * + */ std::mutex m_buffer_mutex; }; diff --git a/includes/cpp_redis/network/tcp_client.hpp b/includes/cpp_redis/network/tcp_client.hpp index 62a1d175..ad5fcc9b 100644 --- a/includes/cpp_redis/network/tcp_client.hpp +++ b/includes/cpp_redis/network/tcp_client.hpp @@ -31,80 +31,96 @@ namespace cpp_redis { namespace network { -//! -//! implementation of the tcp_client_iface based on tacopie networking library -//! +/** + * implementation of the tcp_client_iface based on tacopie networking library + * + */ class tcp_client : public tcp_client_iface { public: - //! ctor - tcp_client(void) = default; - //! dtor - ~tcp_client(void) = default; +/** + * ctor + * + */ + tcp_client() = default; +/** + * dtor + * + */ + ~tcp_client() override = default; public: - //! - //! start the tcp client - //! - //! \param addr host to be connected to - //! \param port port to be connected to - //! \param timeout_ms max time to connect in ms - //! - void connect(const std::string& addr, std::uint32_t port, std::uint32_t timeout_ms); - - //! - //! stop the tcp client - //! - //! \param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. - //! - void disconnect(bool wait_for_removal = false); - - //! - //! \return whether the client is currently connected or not - //! - bool is_connected(void) const; - - //! - //! set number of io service workers for the io service monitoring this tcp connection - //! - //! \param nb_threads number of threads to be assigned - //! +/** + * start the tcp client + * + * @param addr host to be connected to + * @param port port to be connected to + * @param timeout_ms max time to connect in ms + * + */ + void connect(const std::string& addr, std::uint32_t port, std::uint32_t timeout_ms) override; + +/** + * stop the tcp client + * + * @param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. + * + */ + void disconnect(bool wait_for_removal = false) override; + +/** + * @return whether the client is currently connected or not + * + */ + bool is_connected() const override; + +/** + * set number of io service workers for the io service monitoring this tcp connection + * + * @param nb_threads number of threads to be assigned + * + */ void set_nb_workers(std::size_t nb_threads); public: - //! - //! async read operation - //! - //! \param request information about what should be read and what should be done after completion - //! - void async_read(read_request& request); - - //! - //! async write operation - //! - //! \param request information about what should be written and what should be done after completion - //! - void async_write(write_request& request); +/** + * async read operation + * + * @param request information about what should be read and what should be done after completion + * + */ + void async_read(read_request& request) override; + +/** + * async write operation + * + * @param request information about what should be written and what should be done after completion + * + */ + void async_write(write_request& request) override; public: - //! - //! set on disconnection handler - //! - //! \param disconnection_handler handler to be called in case of a disconnection - //! - void set_on_disconnection_handler(const disconnection_handler_t& disconnection_handler); +/** + * set on disconnection handler + * + * @param disconnection_handler handler to be called in case of a disconnection + * + */ + void set_on_disconnection_handler(const disconnection_handler_t& disconnection_handler) override; private: - //! - //! tcp client for redis connection - //! +/** + * tcp client for redis connection + * + */ tacopie::tcp_client m_client; }; -//! -//! set the number of workers to be assigned for the default io service -//! -//! \param nb_threads the number of threads to be assigned -//! +/** + * set the number of workers to be assigned for the default io service + * + * @param nb_threads the number of threads to be assigned + * + */ void set_default_nb_workers(std::size_t nb_threads); } // namespace network diff --git a/includes/cpp_redis/network/tcp_client_iface.hpp b/includes/cpp_redis/network/tcp_client_iface.hpp index 2ba7da06..332f257a 100644 --- a/includes/cpp_redis/network/tcp_client_iface.hpp +++ b/includes/cpp_redis/network/tcp_client_iface.hpp @@ -31,139 +31,167 @@ namespace cpp_redis { namespace network { -//! -//! interface defining how tcp client should be implemented to be used inside cpp_redis -//! +/** + * interface defining how tcp client should be implemented to be used inside cpp_redis + * + */ class tcp_client_iface { public: - //! ctor - tcp_client_iface(void) = default; - //! dtor - virtual ~tcp_client_iface(void) = default; +/** + * ctor + * + */ + tcp_client_iface() = default; +/** + * dtor + * + */ + virtual ~tcp_client_iface() = default; public: - //! - //! start the tcp client - //! - //! \param addr host to be connected to - //! \param port port to be connected to - //! \param timeout_ms max time to connect in ms - //! +/** + * start the tcp client + * + * @param addr host to be connected to + * @param port port to be connected to + * @param timeout_ms max time to connect in ms + * + */ virtual void connect(const std::string& addr, std::uint32_t port, std::uint32_t timeout_ms = 0) = 0; - //! - //! stop the tcp client - //! - //! \param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. - //! +/** + * stop the tcp client + * + * @param wait_for_removal when sets to true, disconnect blocks until the underlying TCP client has been effectively removed from the io_service and that all the underlying callbacks have completed. + * + */ virtual void disconnect(bool wait_for_removal = false) = 0; - //! - //! \return whether the client is currently connected or not - //! - virtual bool is_connected(void) const = 0; +/** + * @return whether the client is currently connected or not + * + */ + virtual bool is_connected() const = 0; public: - //! - //! structure to store read requests result - //! +/** + * structure to store read requests result + * + */ struct read_result { - //! - //! whether the operation succeeded or not - //! +/** + * whether the operation succeeded or not + * + */ bool success; - //! - //! read bytes - //! +/** + * read bytes + * + */ std::vector buffer; }; - //! - //! structure to store write requests result - //! +/** + * structure to store write requests result + * + */ struct write_result { - //! - //! whether the operation succeeded or not - //! +/** + * whether the operation succeeded or not + * + */ bool success; - //! - //! number of bytes written - //! +/** + * number of bytes written + * + */ std::size_t size; }; public: - //! - //! async read completion callbacks - //! function taking read_result as a parameter - //! +/** + * async read completion callbacks + * function taking read_result as a parameter + * + */ typedef std::function async_read_callback_t; - //! - //! async write completion callbacks - //! function taking write_result as a parameter - //! +/** + * async write completion callbacks + * function taking write_result as a parameter + * + */ typedef std::function async_write_callback_t; public: - //! - //! structure to store read requests information - //! +/** + * structure to store read requests information + * + */ struct read_request { - //! - //! number of bytes to read - //! +/** + * number of bytes to read + * + */ std::size_t size; - //! - //! callback to be called on operation completion - //! +/** + * callback to be called on operation completion + * + */ async_read_callback_t async_read_callback; }; - //! - //! structure to store write requests information - //! +/** + * structure to store write requests information + * + */ struct write_request { - //! - //! bytes to write - //! +/** + * bytes to write + * + */ std::vector buffer; - //! - //! callback to be called on operation completion - //! +/** + * callback to be called on operation completion + * + */ async_write_callback_t async_write_callback; }; public: - //! - //! async read operation - //! - //! \param request information about what should be read and what should be done after completion - //! +/** + * async read operation + * + * @param request information about what should be read and what should be done after completion + * + */ virtual void async_read(read_request& request) = 0; - //! - //! async write operation - //! - //! \param request information about what should be written and what should be done after completion - //! +/** + * async write operation + * + * @param request information about what should be written and what should be done after completion + * + */ virtual void async_write(write_request& request) = 0; public: - //! - //! disconnection handler - //! +/** + * disconnection handler + * + */ typedef std::function disconnection_handler_t; - //! - //! set on disconnection handler - //! - //! \param disconnection_handler handler to be called in case of a disconnection - //! +/** + * set on disconnection handler + * + * @param disconnection_handler handler to be called in case of a disconnection + * + */ virtual void set_on_disconnection_handler(const disconnection_handler_t& disconnection_handler) = 0; }; diff --git a/sources/core/client.cpp b/sources/core/client.cpp index 3383882f..0c4e5790 100644 --- a/sources/core/client.cpp +++ b/sources/core/client.cpp @@ -41,17 +41,23 @@ namespace cpp_redis { } client::~client() { - //! ensure we stopped reconnection attempts +/** + * ensure we stopped reconnection attempts + */ if (!m_cancel) { cancel_reconnect(); } - //! If for some reason sentinel is connected then disconnect now. +/** + * If for some reason sentinel is connected then disconnect now. + */ if (m_sentinel.is_connected()) { m_sentinel.disconnect(true); } - //! disconnect underlying tcp socket +/** + * disconnect underlying tcp socket + */ if (m_client.is_connected()) { m_client.disconnect(true); } @@ -66,10 +72,14 @@ namespace cpp_redis { std::uint32_t timeout_ms, std::int32_t max_reconnects, std::uint32_t reconnect_interval_ms) { - //! Save for auto reconnects +/** + * Save for auto reconnects + */ m_master_name = name; - //! We rely on the sentinel to tell us which redis server is currently the master. +/** + * We rely on the sentinel to tell us which redis server is currently the master. + */ if (m_sentinel.get_master_addr_by_name(name, m_redis_server, m_redis_port, true)) { connect(m_redis_server, m_redis_port, connect_callback, timeout_ms, max_reconnects, reconnect_interval_ms); } else { @@ -87,14 +97,18 @@ namespace cpp_redis { std::uint32_t reconnect_interval_ms) { __CPP_REDIS_LOG(debug, "cpp_redis::client attempts to connect"); - //! Save for auto reconnects +/** + * Save for auto reconnects + */ m_redis_server = host; m_redis_port = port; m_connect_callback = connect_callback; m_max_reconnects = max_reconnects; m_reconnect_interval_ms = reconnect_interval_ms; - //! notify start +/** + * notify start + */ if (m_connect_callback) { m_connect_callback(host, port, connect_state::start); } @@ -106,7 +120,9 @@ namespace cpp_redis { __CPP_REDIS_LOG(info, "cpp_redis::client connected"); - //! notify end +/** + * notify end + */ if (m_connect_callback) { m_connect_callback(m_redis_server, m_redis_port, connect_state::ok); } @@ -116,10 +132,14 @@ namespace cpp_redis { client::disconnect(bool wait_for_removal) { __CPP_REDIS_LOG(debug, "cpp_redis::client attempts to disconnect"); - //! close connection +/** + * close connection + */ m_client.disconnect(wait_for_removal); - //! make sure we clear buffer of unsent commands +/** + * make sure we clear buffer of unsent commands + */ clear_callbacks(); __CPP_REDIS_LOG(info, "cpp_redis::client disconnected"); @@ -177,11 +197,15 @@ namespace cpp_redis { m_commands.push({redis_cmd, callback}); } -//! commit pipelined transaction +/** + * commit pipelined transaction + */ client & client::commit() { - //! no need to call commit in case of reconnection - //! the reconnection flow will do it for us +/** + * no need to call commit in case of reconnection + * the reconnection flow will do it for us + */ if (!is_reconnecting()) { try_commit(); } @@ -191,8 +215,10 @@ namespace cpp_redis { client & client::sync_commit() { - //! no need to call commit in case of reconnection - //! the reconnection flow will do it for us +/** + * no need to call commit in case of reconnection + * the reconnection flow will do it for us + */ if (!is_reconnecting()) { try_commit(); } @@ -213,7 +239,9 @@ namespace cpp_redis { } catch (const cpp_redis::redis_error &) { __CPP_REDIS_LOG(error, "cpp_redis::client could not send pipelined commands"); - //! ensure commands are flushed + /** + * ensure commands are flushed + */ clear_callbacks(); throw; } @@ -252,7 +280,9 @@ namespace cpp_redis { return; } - //! dequeue commands and move them to a local variable +/** + * dequeue commands and move them to a local variable + */ std::queue commands = std::move(m_commands); m_callbacks_running += __CPP_REDIS_LENGTH(commands.size()); @@ -281,11 +311,15 @@ namespace cpp_redis { return; } - //! dequeue commands and move them to a local variable +/** + * dequeue commands and move them to a local variable + */ std::queue commands = std::move(m_commands); while (!commands.empty()) { - //! Reissue the pending command and its callback. +/** + * Reissue the pending command and its callback. + */ unprotected_send(commands.front().command, commands.front().callback); commands.pop(); @@ -294,12 +328,16 @@ namespace cpp_redis { void client::connection_disconnection_handler(network::redis_connection &) { - //! leave right now if we are already dealing with reconnection +/** + * leave right now if we are already dealing with reconnection + */ if (is_reconnecting()) { return; } - //! initiate reconnection process +/** + * initiate reconnection process + */ m_reconnecting = true; m_current_reconnect_attempts = 0; @@ -309,7 +347,9 @@ namespace cpp_redis { m_connect_callback(m_redis_server, m_redis_port, connect_state::dropped); } - //! Lock the callbacks mutex of the base class to prevent more client commands from being issued until our reconnect has completed. +/** + * Lock the callbacks mutex of the base class to prevent more client commands from being issued until our reconnect has completed. + */ std::lock_guard lock_callback(m_callbacks_mutex); while (should_reconnect()) { @@ -320,13 +360,17 @@ namespace cpp_redis { if (!is_connected()) { clear_callbacks(); - //! Tell the user we gave up! +/** + * Tell the user we gave up! + */ if (m_connect_callback) { m_connect_callback(m_redis_server, m_redis_port, connect_state::stopped); } } - //! terminate reconnection +/** + * terminate reconnection + */ m_reconnecting = false; } @@ -380,11 +424,15 @@ namespace cpp_redis { void client::reconnect() { - //! increase the number of attempts to reconnect +/** + * increase the number of attempts to reconnect + */ ++m_current_reconnect_attempts; - //! We rely on the sentinel to tell us which redis server is currently the master. +/** + * We rely on the sentinel to tell us which redis server is currently the master. + */ if (!m_master_name.empty() && !m_sentinel.get_master_addr_by_name(m_master_name, m_redis_server, m_redis_port, true)) { if (m_connect_callback) { @@ -393,7 +441,9 @@ namespace cpp_redis { return; } - //! Try catch block because the redis client throws an error if connection cannot be made. +/** + * Try catch block because the redis client throws an error if connection cannot be made. + */ try { connect(m_redis_server, m_redis_port, m_connect_callback, m_connect_timeout_ms, m_max_reconnects, m_reconnect_interval_ms); @@ -408,7 +458,9 @@ namespace cpp_redis { return; } - //! notify end +/** + * notify end + */ if (m_connect_callback) { m_connect_callback(m_redis_server, m_redis_port, connect_state::ok); } @@ -494,10 +546,10 @@ namespace cpp_redis { return {bitfield_operation_type::incrby, type, offset, increment, overflow}; } -//! -//! Redis commands -//! Callback-based -//! +/** + * Redis commands + * Callback-based + */ client & client::append(const std::string &key, const std::string &value, const reply_callback_t &reply_callback) { @@ -516,9 +568,13 @@ namespace cpp_redis { void client::unprotected_auth(const std::string &password, const reply_callback_t &reply_callback) { - //! save the password for reconnect attempts. +/** + * save the password for reconnect attempts. + */ m_password = password; - //! store command in pipeline +/** + * store command in pipeline + */ unprotected_send({"AUTH", password}, reply_callback); } @@ -1042,37 +1098,51 @@ namespace cpp_redis { std::vector cmd = {"GEORADIUS", key, std::to_string(longitude), std::to_string(latitude), std::to_string(radius), geo_unit_to_string(unit)}; - //! with_coord (optional) +/** + * with_coord (optional) + */ if (with_coord) { cmd.emplace_back("WITHCOORD"); } - //! with_dist (optional) +/** + * with_dist (optional) + */ if (with_dist) { cmd.emplace_back("WITHDIST"); } - //! with_hash (optional) +/** + * with_hash (optional) + */ if (with_hash) { cmd.emplace_back("WITHHASH"); } - //! order (optional) +/** + * order (optional) + */ cmd.emplace_back(asc_order ? "ASC" : "DESC"); - //! count (optional) +/** + * count (optional) + */ if (count > 0) { cmd.emplace_back("COUNT"); cmd.push_back(std::to_string(count)); } - //! store_key (optional) +/** + * store_key (optional) + */ if (!store_key.empty()) { cmd.emplace_back("STOREDIST"); cmd.push_back(storedist_key); } - //! storedist_key (optional) +/** + * storedist_key (optional) + */ if (!storedist_key.empty()) { cmd.emplace_back("STOREDIST"); cmd.push_back(storedist_key); @@ -1130,37 +1200,51 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"GEORADIUSBYMEMBER", key, member, std::to_string(radius), geo_unit_to_string(unit)}; - //! with_coord (optional) +/** + * with_coord (optional) + */ if (with_coord) { cmd.emplace_back("WITHCOORD"); } - //! with_dist (optional) +/** + * with_dist (optional) + */ if (with_dist) { cmd.emplace_back("WITHDIST"); } - //! with_hash (optional) +/** + * with_hash (optional) + */ if (with_hash) { cmd.emplace_back("WITHHASH"); } - //! order (optional) +/** + * order (optional) + */ cmd.emplace_back(asc_order ? "ASC" : "DESC"); - //! count (optional) +/** + * count (optional) + */ if (count > 0) { cmd.emplace_back("COUNT"); cmd.push_back(std::to_string(count)); } - //! store_key (optional) +/** + * store_key (optional) + */ if (!store_key.empty()) { cmd.emplace_back("STOREDIST"); cmd.push_back(storedist_key); } - //! storedist_key (optional) +/** + * storedist_key (optional) + */ if (!storedist_key.empty()) { cmd.emplace_back("STOREDIST"); cmd.push_back(storedist_key); @@ -1796,9 +1880,13 @@ namespace cpp_redis { void client::unprotected_select(int index, const reply_callback_t &reply_callback) { - //! save the index of the database for reconnect attempts. +/** + * save the index of the database for reconnect attempts. + */ m_database_index = index; - //! save command in the pipeline +/** + * save command in the pipeline + */ unprotected_send({"SELECT", std::to_string(index)}, reply_callback); } @@ -1989,20 +2077,26 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"SORT", key}; - //! add by pattern (optional) +/** + * add by pattern (optional) + */ if (!by_pattern.empty()) { cmd.emplace_back("BY"); cmd.push_back(by_pattern); } - //! add limit (optional) +/** + * add limit (optional) + */ if (limit) { cmd.emplace_back("LIMIT"); cmd.push_back(std::to_string(offset)); cmd.push_back(std::to_string(count)); } - //! add get pattern (optional) +/** + * add get pattern (optional) + */ for (const auto &get_pattern : get_patterns) { if (get_pattern.empty()) { continue; @@ -2012,15 +2106,21 @@ namespace cpp_redis { cmd.push_back(get_pattern); } - //! add order by (optional) +/** + * add order by (optional) + */ cmd.emplace_back(asc_order ? "ASC" : "DESC"); - //! add alpha (optional) +/** + * add alpha (optional) + */ if (alpha) { cmd.emplace_back("ALPHA"); } - //! add store dest (optional) +/** + * add store dest (optional) + */ if (!store_dest.empty()) { cmd.emplace_back("STORE"); cmd.push_back(store_dest); @@ -2170,7 +2270,9 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"XACK", stream, group}; - //! ids +/** + * ids + */ for (auto &id : message_ids) { cmd.push_back(id); } @@ -2184,7 +2286,9 @@ namespace cpp_redis { const std::multimap &field_members, const reply_callback_t &reply_callback) { std::vector cmd = {"XADD", key, id}; - //! score members +/** + * score members + */ for (auto &sm : field_members) { cmd.push_back(sm.first); cmd.push_back(sm.second); @@ -2199,7 +2303,9 @@ namespace cpp_redis { const std::vector &message_ids, const xclaim_options_t &options, const reply_callback_t &reply_callback) { std::vector cmd = {"XCLAIM", stream, group, consumer, std::to_string(min_idle_time)}; - //! ids +/** + * ids + */ for (auto &id : message_ids) { cmd.push_back(id); } @@ -2231,7 +2337,9 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"XDEL", key}; - //! ids +/** + * ids + */ for (auto &id : id_members) { cmd.push_back(id); } @@ -2422,10 +2530,14 @@ namespace cpp_redis { const std::multimap &score_members, const reply_callback_t &reply_callback) { std::vector cmd = {"ZADD", key}; - //! options +/** + * options + */ cmd.insert(cmd.end(), options.begin(), options.end()); - //! score members +/** + * score members + */ for (auto &sm : score_members) { cmd.push_back(sm.first); cmd.push_back(sm.second); @@ -2486,12 +2598,16 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"ZINTERSTORE", destination, std::to_string(numkeys)}; - //! keys +/** + * keys + */ for (const auto &key : keys) { cmd.push_back(key); } - //! weights (optional) +/** + * weights (optional) + */ if (!weights.empty()) { cmd.emplace_back("WEIGHTS"); @@ -2500,7 +2616,9 @@ namespace cpp_redis { } } - //! aggregate method +/** + * aggregate method + */ if (method != aggregate_method::server_default) { cmd.emplace_back("AGGREGATE"); cmd.push_back(aggregate_method_to_string(method)); @@ -2652,12 +2770,16 @@ namespace cpp_redis { std::size_t offset, std::size_t count, bool withscores, const reply_callback_t &reply_callback) { std::vector cmd = {"ZRANGEBYLEX", key, min, max}; - //! withscores (optional) +/** + * withscores (optional) + */ if (withscores) { cmd.emplace_back("WITHSCORES"); } - //! limit (optional) +/** + * limit (optional) + */ if (limit) { cmd.emplace_back("LIMIT"); cmd.push_back(std::to_string(offset)); @@ -2746,12 +2868,16 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"ZRANGEBYSCORE", key, min, max}; - //! withscores (optional) +/** + * withscores (optional) + */ if (withscores) { cmd.emplace_back("WITHSCORES"); } - //! limit (optional) +/** + * limit (optional) + */ if (limit) { cmd.emplace_back("LIMIT"); cmd.push_back(std::to_string(offset)); @@ -2961,12 +3087,16 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"ZREVRANGEBYLEX", key, max, min}; - //! withscores (optional) +/** + * withscores (optional) + */ if (withscores) { cmd.emplace_back("WITHSCORES"); } - //! limit (optional) +/** + * limit (optional) + */ if (limit) { cmd.emplace_back("LIMIT"); cmd.push_back(std::to_string(offset)); @@ -3055,12 +3185,16 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"ZREVRANGEBYSCORE", key, max, min}; - //! withscores (optional) +/** + * withscores (optional) + */ if (withscores) { cmd.emplace_back("WITHSCORES"); } - //! limit (optional) +/** + * limit (optional) + */ if (limit) { cmd.emplace_back("LIMIT"); cmd.push_back(std::to_string(offset)); @@ -3124,12 +3258,16 @@ namespace cpp_redis { const reply_callback_t &reply_callback) { std::vector cmd = {"ZUNIONSTORE", destination, std::to_string(numkeys)}; - //! keys +/** + * keys + */ for (const auto &key : keys) { cmd.push_back(key); } - //! weights (optional) +/** + * weights (optional) + */ if (!weights.empty()) { cmd.emplace_back("WEIGHTS"); @@ -3138,7 +3276,9 @@ namespace cpp_redis { } } - //! aggregate method +/** + * aggregate method + */ if (method != aggregate_method::server_default) { cmd.emplace_back("AGGREGATE"); cmd.push_back(aggregate_method_to_string(method)); @@ -3148,10 +3288,10 @@ namespace cpp_redis { return *this; } -//! -//! Redis Commands -//! std::future-based -//! +/** + * Redis Commands + * std::future-based + */ std::future client::exec_cmd(const std::function &f) { diff --git a/sources/core/consumer.cpp b/sources/core/consumer.cpp index 0a17385d..bb30eae6 100644 --- a/sources/core/consumer.cpp +++ b/sources/core/consumer.cpp @@ -1,7 +1,3 @@ -#include - -#include - // The MIT License (MIT) // // Copyright (c) 11/27/18 nick. @@ -24,106 +20,143 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE.#include "consumer.hpp" +#include +#include -#include +using std::bind; +using namespace std::placeholders; namespace cpp_redis { + consumer_client_container::consumer_client_container() : ack_client(), poll_client() { + } + consumer::consumer(std::string stream, std::string consumer, size_t max_concurrency) : m_stream(std::move(stream)), m_name(std::move(consumer)), + m_read_id("0"), + m_block_sec(-1), m_max_concurrency(max_concurrency), - m_task_queue(), - m_client(new client()), - m_sub_client(new client()), - m_proc_queue(new dispatch_queue(stream, max_concurrency)) { + m_read_count(static_cast(max_concurrency)), + m_callbacks() { + // Supply the dispatch queue a callback to notify the queue when it is at max capacity + m_dispatch_queue = dispatch_queue_ptr_t( + new dispatch_queue(stream, [&](size_t size) { + dispatch_changed_handler(size); + }, max_concurrency)); + m_client = client_container_ptr_t(new consumer_client_container()); } consumer &cpp_redis::consumer::subscribe(const std::string &group, const consumer_callback_t &consumer_callback, const acknowledgement_callback_t &acknowledgement_callback) { - std::unique_lock task_queue_lock(m_task_queue_mutex); - m_task_queue[group] = {consumer_callback, acknowledgement_callback}; - task_queue_lock.unlock(); + m_callbacks.insert({group, + {consumer_callback, + acknowledgement_callback}}); return *this; } + void consumer::dispatch_changed_handler(size_t size) { + if (size >= m_max_concurrency) { + dispatch_queue_full.store(true); + dispatch_queue_changed.notify_all(); + } + } + void consumer::connect(const std::string &host, size_t port, const connect_callback_t &connect_callback, uint32_t timeout_ms, int32_t max_reconnects, uint32_t reconnect_interval_ms) { - m_client->connect(host, port, connect_callback, timeout_ms, max_reconnects, reconnect_interval_ms); - m_sub_client->connect(host, port, connect_callback, timeout_ms, max_reconnects, reconnect_interval_ms); + m_client->ack_client.connect(host, port, connect_callback, timeout_ms, max_reconnects, reconnect_interval_ms); + m_client->poll_client.connect(host, port, connect_callback, timeout_ms, max_reconnects, reconnect_interval_ms); } - consumer &consumer::commit() { - //std::thread p([&]() { - // Set the consumer id to 0 so that we start with failed messages - std::string consumer_name = "0"; + void consumer::auth(const std::string &password, + const reply_callback_t &reply_callback) { + m_client->ack_client.auth(password, reply_callback); + m_client->poll_client.auth(password, reply_callback); + } - std::unique_lock cv_mutex_lock(m_cv_mutex); + consumer &consumer::commit() { while (!is_ready) { - if (!is_ready) - if (m_max_concurrency <= m_proc_queue->size()) - m_cv.wait(cv_mutex_lock); - - std::lock_guard task_queue_lock(m_task_queue_mutex); - for (auto &q : m_task_queue) { - //task_queue_lock.lock(); - auto group = q.first; - auto cb_container = q.second; - //task_queue_lock.unlock(); - m_sub_client->xreadgroup({group, consumer_name, {{m_stream}, {">"}}, 1, -1, false} // count, block, no_ack - , [&](cpp_redis::reply &reply) { - cpp_redis::xstream_reply xs(reply); - if (xs.empty()) { - if (consumer_name == "0") { - consumer_name = m_name; - } - } else { - m_reply_queue.push(reply); - m_q_status.notify_one(); - //m_proc_queue->dispatch(fp_) - //process(); - } - }); - m_sub_client->sync_commit(); + if (!is_ready) { + std::unique_lock dispatch_lock(dispatch_queue_changed_mutex); + dispatch_queue_changed.wait(dispatch_lock, [&]() { + return !dispatch_queue_full.load(); + }); + m_read_count = static_cast(m_max_concurrency - m_dispatch_queue->size()); + poll(); } } - //}); return *this; } - void consumer::process() { - std::unique_lock m_q_status_lock(m_q_status_mutex); - m_q_status.wait(m_q_status_lock, [this]() { return !m_reply_queue.empty(); }); - - auto r = m_reply_queue.back(); - m_reply_queue.pop(); - - xstream_reply xs(r); - for (auto &r : xs) { - for (auto &m : r.Messages) { - try { - std::string group_id = m.get_id(); - auto task = m_task_queue.find(group_id); - auto callback_container = task->second; - - auto callback = [&](const message_type &message) { - auto response = callback_container.consumer_callback(message); - m_client->xack(m_stream, group_id, {m.get_id()}, [&](const reply &r) { - if (r.is_integer()) - callback_container.acknowledgement_callback(r.as_integer()); - }); - m_client->sync_commit(); - return response; - }; - m_proc_queue->dispatch(m, callback); - } catch (std::exception &exc) { - __CPP_REDIS_LOG(1, "Processing failed for message id: " + m.get_id() + "\nDetails: " + exc.what()); - throw exc; - } - } + void consumer::poll() { + for (auto &cb : m_callbacks) { + m_client->poll_client.xreadgroup( + { + cb.first, m_name, + { + {m_stream}, + {m_read_id} + }, + m_read_count, m_block_sec, false + }, + [&](cpp_redis::reply &reply) { + // The reply is an array if valid + cpp_redis::xstream_reply s_reply(reply); + if (!s_reply.is_null()) { + __CPP_REDIS_LOG(2, "Stream " << s_reply) + for (const auto &stream : s_reply) { + for (auto &m : stream.Messages) { + if (m_should_read_pending.load()) + m_read_id = m.get_id(); + try { + m_dispatch_queue->dispatch( + m, + [&](const message_type &message) { + auto response = cb.second.consumer_callback(message); + + // add results to result stream + m_client->ack_client.xadd( + m_stream+":results", + "*", + response); + + // acknowledge task completion + m_client->ack_client.xack( + m_stream, + cb.first, + {message.get_id()}, + [&](const reply_t &r) { + if (r.is_integer()) { + auto ret_int = r.as_integer(); + cb.second.acknowledgement_callback(ret_int); + } + }).sync_commit(); + return response; + }); + } catch (std::exception &exc) { + __CPP_REDIS_LOG(1, + "Processing failed for message id: " + m.get_id() + + "\nDetails: " + exc.what()); + throw exc; + } + } + } + } else { + if (m_should_read_pending.load()) { + m_should_read_pending.store(false); + m_read_id = ">"; + // Set to block infinitely + m_block_sec = 0; + // Set to read 1 + m_read_count = 1; + } + } + return; + }).sync_commit(); //(std::chrono::milliseconds(1000)); } } + } // namespace cpp_redis diff --git a/sources/core/reply.cpp b/sources/core/reply.cpp index 12ff1516..d024dbc6 100644 --- a/sources/core/reply.cpp +++ b/sources/core/reply.cpp @@ -182,34 +182,7 @@ namespace cpp_redis { return m_type; } - std::ostream &operator<<(std::ostream &os, const reply &reply) { - switch (reply.get_type()) { - case cpp_redis::reply::type::error: - os << reply.error(); - break; - case cpp_redis::reply::type::bulk_string: - os << reply.as_string(); - break; - case cpp_redis::reply::type::simple_string: - os << reply.as_string(); - break; - case cpp_redis::reply::type::null: - os << std::string("(nil)"); - break; - case cpp_redis::reply::type::integer: - os << reply.as_integer(); - break; - case cpp_redis::reply::type::array: - for (const auto &item : reply.as_array()) - os << item; - break; - } - - return os; - } - } // namespace cpp_redis -/* std::ostream & operator<<(std::ostream &os, const cpp_redis::reply &reply) { @@ -237,4 +210,3 @@ operator<<(std::ostream &os, const cpp_redis::reply &reply) { return os; } -*/ diff --git a/sources/core/sentinel.cpp b/sources/core/sentinel.cpp index c47a7e1b..883eccd0 100644 --- a/sources/core/sentinel.cpp +++ b/sources/core/sentinel.cpp @@ -251,7 +251,9 @@ sentinel::send(const std::vector& redis_cmd, const reply_callback_t return *this; } -//! commit pipelined transaction +/** + * commit pipelined transaction + */ sentinel& sentinel::commit(void) { try_commit(); diff --git a/sources/core/types.cpp b/sources/core/types.cpp index 7d0c5da8..fec903d6 100644 --- a/sources/core/types.cpp +++ b/sources/core/types.cpp @@ -1,48 +1,65 @@ -/* - * - * Created by nick on 11/23/18. - * - * Copyright(c) 2018 Iris. All rights reserved. - * - * Use and copying of this software and preparation of derivative - * works based upon this software are not permitted. Any distribution - * of this software or derivative works must comply with all applicable - * Canadian export control laws. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL IRIS OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - +// The MIT License (MIT) +// +// Copyright (c) 2015-2017 Simon Ninon +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. #include namespace cpp_redis { xmessage::xmessage(const reply_t &data) { - auto d = data.as_array(); - set_id(d[0].as_string()); - int i = 0; - std::string key; - auto value_arr = d[1].as_array(); - push(value_arr.begin(), value_arr.end()); + if (data.is_array()) { + auto k = data.as_array().front(); + auto v = data.as_array().back(); + if (k.is_string()) { + set_id(k.as_string()); + } + if (v.is_array()) { + auto val_array = v.as_array(); + std::string key; + int i = 1; + for (auto &val : val_array) { + if (i%2!=0) { + key = val.as_string(); + } else { + push(key, val); + } + i++; + } + //push(v.as_array().begin(), v.as_array().end()); + } + } } xstream::xstream(const reply &data) { - auto d = data.as_array(); - Stream = d[0].as_string(); - for (auto &s : d[1].as_array()) { - Messages.emplace_back(s); + if (data.is_array()) { + auto s = data.as_array().front(); + if (s.is_string()) { + Stream = s.as_string(); + } + auto m = data.as_array().back(); + if (m.is_array()) { + for (auto &sm : m.as_array()) { + Messages.emplace_back(xmessage(sm)); + } + } } } @@ -50,7 +67,7 @@ namespace cpp_redis { os << "\n\t\t\"id\": " << xm.get_id() << "\n\t\t\"values\": {"; for (auto &v : xm.get_values()) { auto re = v.second; - os << "\n\t\t\t\"" << v.first << "\": " << re << ","; + os << "\n\t\t\t\"" << v.first << "\": " << re.as_string() << ","; } os << "\n\t\t}"; return os; @@ -68,11 +85,10 @@ namespace cpp_redis { } xstream_reply::xstream_reply(const reply &data) { - if (data.is_null()) { - return; - } - for (auto &d : data.as_array()) { - emplace_back(xstream(d)); + if (data.is_array()) { + for (auto &d : data.as_array()) { + emplace_back(xstream(d)); + } } } diff --git a/sources/misc/dispatch_queue.cpp b/sources/misc/dispatch_queue.cpp index 5e5b151c..b401971a 100644 --- a/sources/misc/dispatch_queue.cpp +++ b/sources/misc/dispatch_queue.cpp @@ -1,14 +1,12 @@ +#include + /* - * * Created by nick on 11/22/18. - * * Copyright(c) 2018 Iris. All rights reserved. - * * Use and copying of this software and preparation of derivative * works based upon this software are not permitted. Any distribution * of this software or derivative works must comply with all applicable * Canadian export control laws. - * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE @@ -21,15 +19,14 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * */ #include namespace cpp_redis { - dispatch_queue::dispatch_queue(std::string name, size_t thread_cnt) : - m_name(name), m_threads(thread_cnt) { + dispatch_queue::dispatch_queue(std::string name, const notify_callback_t ¬ify_callback, size_t thread_cnt) : + m_name(name), m_threads(thread_cnt), m_mq(), notify_handler(notify_callback) { printf("Creating dispatch queue: %s\n", name.c_str()); printf("Dispatch threads: %zu\n", thread_cnt); @@ -85,6 +82,8 @@ namespace cpp_redis { return (!m_mq.empty() || m_quit); }); + notify_handler(m_mq.size()); + //after wait, we own the lock if (!m_quit && !m_mq.empty()) { auto op = std::move(m_mq.front()); @@ -93,6 +92,12 @@ namespace cpp_redis { //unlock now that we're done messing with the queue lock.unlock(); + auto vals = op.message.get_values(); + + for (auto v : vals) { + std::cout << v.second << std::endl; + } + auto res = op.callback(op.message); lock.lock(); diff --git a/sources/network/redis_connection.cpp b/sources/network/redis_connection.cpp index 4d43316e..20ac6305 100644 --- a/sources/network/redis_connection.cpp +++ b/sources/network/redis_connection.cpp @@ -25,7 +25,9 @@ #include #ifndef __CPP_REDIS_USE_CUSTOM_TCP_CLIENT + #include + #endif /* __CPP_REDIS_USE_CUSTOM_TCP_CLIENT */ namespace cpp_redis { @@ -58,11 +60,15 @@ namespace cpp_redis { try { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to connect"); - //! connect client +/** + * connect client + */ m_client->connect(host, (uint32_t) port, timeout_ms); m_client->set_on_disconnection_handler(std::bind(&redis_connection::tcp_client_disconnection_handler, this)); - //! start to read asynchronously +/** + * start to read asynchronously + */ tcp_client_iface::read_request request = {__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}; @@ -83,12 +89,18 @@ namespace cpp_redis { redis_connection::disconnect(bool wait_for_removal) { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to disconnect"); - //! close connection +/** + * close connection + */ m_client->disconnect(wait_for_removal); - //! clear buffer +/** + * clear buffer + */ m_buffer.clear(); - //! clear builder +/** + * clear builder + */ m_builder.reset(); __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection disconnected"); @@ -119,12 +131,16 @@ namespace cpp_redis { return *this; } -//! commit pipelined transaction + /** + * commit pipelined transaction + */ redis_connection & redis_connection::commit() { std::lock_guard lock(m_buffer_mutex); - //! ensure buffer is cleared + /** + * ensure buffer is cleared + */ __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to send pipelined commands"); std::string buffer = std::move(m_buffer); @@ -184,18 +200,26 @@ namespace cpp_redis { m_client->async_read(request); } catch (const std::exception &) { - //! Client disconnected in the meantime +/** + * Client disconnected in the meantime + */ } } void redis_connection::tcp_client_disconnection_handler() { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection has been disconnected"); - //! clear buffer +/** + * clear buffer + */ m_buffer.clear(); - //! clear builder +/** + * clear builder + */ m_builder.reset(); - //! call disconnection handler +/** + * call disconnection handler + */ call_disconnection_handler(); } diff --git a/tests/sources/main.cpp b/tests/sources/main.cpp index f17e6db3..3ab8ef44 100644 --- a/tests/sources/main.cpp +++ b/tests/sources/main.cpp @@ -25,20 +25,47 @@ #ifdef _WIN32 #include #endif /* _WIN32 */ - -//! For debugging purpose, uncomment +$1/** + *$3 + *$5 + *$7 + *$9 + *$11 + *$13 + *$15 + *$17 + *$19 + */ // #include // #include // #include int -main(int argc, char **argv) { - //! For debugging purpose, uncomment +$1/** + *$3 + *$5 + *$7 + *$9 + *$11 + *$13 + *$15 + *$17 + *$19 + */ // cpp_redis::active_logger = std::unique_ptr(new cpp_redis::logger(cpp_redis::logger::log_level::debug)); // tacopie::active_logger = std::unique_ptr(new tacopie::logger(tacopie::logger::log_level::debug)); -#ifdef _WIN32 - //! Windows network DLL init +$1/** + *$3 + *$5 + *$7 + *$9 + *$11 + *$13 + *$15 + *$17 + *$19 + */ WORD version = MAKEWORD(2, 2); WSADATA data;