basiliscos / cpp-bredis

Boost::ASIO low-level redis client (connector)
MIT License
147 stars 36 forks source link

using as cpp-bredis #40

Closed giorgiozoppi closed 3 years ago

giorgiozoppi commented 4 years ago

Hello, we are going to use cpp-bredis in our redis graph solution we have the following code:


executor_context_ptr_->conn.async_write(
                        executor_context_ptr_->tx_buff,
                        cmd,
                        asio::bind_executor(executor_context_ptr_->strand, [this, &result_promise](const sys::error_code& ec, std::size_t bytes_transferred)
                            {
                            if (!ec) {
                                auto self = this;
                                self->executor_context_ptr_->tx_buff.consume(bytes_transferred);
                                executor_context_ptr_->conn.async_read(
                                    executor_context_ptr_->rx_buff,
                                    asio::bind_executor(executor_context_ptr_->strand, [this, &result_promise](const sys::error_code& ec, result_t&& r) {
                                        if (!ec) {
                                            auto self = this;
                                            auto result = r.result;
                                            auto& replies = boost::get<r::markers::array_holder_t<Iterator>>(r.result);
                                            // here how to parse the reply?

                                            self->executor_context_ptr_->rx_buff.consume(r.consumed);
                                            // parse and set result.
                                        }
                                        else
                                        {
                                            redisgraph::result_view read_error_view;
                                            result_promise.set_value(read_error_view);
                                        }
                                        }));
                            }

We woud like to know how to parse and debug the list of arrays. Best Regards, Giorgio.

basiliscos commented 4 years ago

Hello Giorgio,

There is no generic answer to your question, as the array response in Redis is recursive structure, i.e. it might be array of arrays of arrays of mixture string/ints/arrays etc.

So, you should know your redis command, and it's expected output. Here are a few examples. Let's assume that array is flat array of strings, that for debug we'd like to dump all of them into cout. It can be done as:

template <typename Iterator>
struct my_dumper : public boost::static_visitor<void> {
    template <typename T> void operator()(const T & /*value*/) const {
        /* NOOP */
    }

    void
    operator()(const r::markers::array_holder_t<Iterator> &value) const {
        for(auto& item: value.elements) {
            auto* str = boost::get<r::markers::string_t<Iterator>>(&item);
            if (str) {
                std::string copy{str->from, str->to};
                std::cout << "item = " << copy << "\n";
            }
        }
    }
};

If you'd like to concatenate all strings are return as std::string it can be done as

template <typename Iterator>
struct my_extractor : public boost::static_visitor<std::string> {
    template <typename T> std::string operator()(const T & /*value*/) const { return ""; }

    std::string operator()(const r::markers::array_holder_t<Iterator> &value) const {
        std::string result;
        for(auto& item: value.elements) {
            auto* str = boost::get<r::markers::string_t<Iterator>>(&item);
            if (str) {
                std::string copy{str->from, str->to};
                result += copy;
            }
        }
        return result;
    }
};

The operators defined above then needs to be applied like :

      my_dumper<Iterator> dumper;
      boost::apply_visitor(dumper, r.result);

      my_extractor<Iterator> extractor;
      auto str = boost::apply_visitor(extractor, r.result);

Please note, there is always the "fail-branch", in the operators (my_dumper and my_extractor) above, i.e. some default behavior when redis replied with error, or, when you expected string but it replied with int (its probably bug in your code).

giorgiozoppi commented 4 years ago

Thanks. Ok. I will try and help out to use it. Your library is pretty good and fast. I have another issue, i want to send out a custom promise and give the possibility to use multiple threads. At the current moment, i set a context and set the value of the promise after the read. Is it the best approach or is it better use your promise example?

for (size_t i = 0; i < workers_count; i++) {
                    threadpool_.emplace_back(std::thread([this]()
                        {
                            io_context_.run();
                        }));
                }  
basiliscos commented 4 years ago

You should do strand-protection of your connection. As soon as it is safe in MT-environment, you can safely read/write to it using bredis

giorgiozoppi commented 4 years ago

Currently my problem is to parse the redisgraph module result. Before starting, do you support https://redis.io/topics/protocol ? How can i log the response structure to standard output? Do you provide already something?

basiliscos commented 4 years ago

Yes, bredis complies redis-protocol. If it not, please send an example.

There are a few techniques for logging/development:

  1. You can try use an proxying socket like here https://github.com/basiliscos/cpp-bredis/blob/master/t/SocketWithLogging.hpp and https://github.com/basiliscos/cpp-bredis/blob/master/t/12-basic-types.cpp#L23

  2. Another, more simple way, is to use Wireshark tool to log what's going on. (i.e. get actual bits)

  3. I'm not sure what is redisgraph, I assume it is other protocol. So, generally, you should write your own layer of redisgraph on top of the bredis, and only then you it in your final app. Otherwise, mixing app-logic and protocol logic might lead to bad code style.