boostorg / redis

An async redis client designed for performance and scalability

Home Page:https://www.boost.org/doc/libs/develop/libs/redis/doc/html/index.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

second call to async_exec hangs and never returns

rotrida opened this issue · comments

Hi,

I'm planning to replace my redis connection with boost.redis using coroutines. I was testing the code bellow but I noticed that it aways freezes at the second call to async_exec. Any idea what I'm doing wrong? My plan is to keep the connection to the database open and re-use it always when is necessary.

auto
receiver(std::shared_ptr conn) -> asio::awaitable
{
request auth_req;
auth_req.push("AUTH", "my_psw");
boost::redis::responsestd::string auth_resp;
co_await conn->async_exec(auth_req, auth_resp, boost::asio::use_awaitable);
std::cout << "AUTH: " << std::get<0>(auth_resp).value() << std::endl;
std::get<0>(auth_resp).value().clear();

request ping_req;
auth_req.push("PING");
boost::redis::responsestd::string ping_resp;
co_await conn->async_exec(ping_req, ping_resp, boost::asio::use_awaitable);
std::cout << "PING: " << std::get<0>(ping_resp).value() << std::endl;
std::get<0>(ping_resp).value().clear();

TL;DR: Pass the credentials to async_run and you should be ok

boost::redis::config cfg;
cfg.username = "username";
cfg.password = "password";
cfg.addr.host = "host";
cfg.addr.port = "port";

conn.async_run(cfg, {}, [](auto ec) { });

I was testing the code bellow but I noticed that it aways freezes at the second call to async_exec. Any idea what I'm doing wrong?

I believe the problem is that the HELLO command that is sent automatically by Boost.Redis is failing due to authentication issues. Setting the credentials as shown above will fix that. If that doesn't work, please call async_run with debug logging level.

conn.async_run(cfg, {boost::redis::logger::level::debug}, [](auto ec) { });

and sent it here so I can analyse the problem.

My plan is to keep the connection to the database open and re-use it always when is necessary.

Yes, that is the correct way of using Boost.Redis. Most of the time you only need one connection.

Hi Marcelo,

I changed the code (bellow) but the situation persists. I noted that if I remove the need of password to connect (requirepass my_psw), everything works. I'm not using username on the configuration, btw.

I also collected the debug info for you to check.

(Boost.Redis) run-all-op: resolve addresses 127.0.0.1:6379
(Boost.Redis) run-all-op: connected to endpoint 127.0.0.1:6379
(Boost.Redis) writer-op: 122 bytes written.
(Boost.Redis) reader-op: 310 bytes read.
(Boost.Redis) hello-op: Success
(Boost.Redis) hello-op: error/canceled. Exiting ...
(Boost.Redis) reader-op: Operation canceled
(Boost.Redis) reader-op: error. Exiting ...
(Boost.Redis) writer-op: canceled (3). Exiting ...
(Boost.Redis) run-op: Operation canceled (reader), Success (writer)
(Boost.Redis) check-timeout-op: Response error. Exiting ...
(Boost.Redis) ping_op: error/cancelled (1).
(Boost.Redis) check-health-op: Operation canceled (async_ping), Success (async_check_timeout).
(Boost.Redis) runner-op: Operation canceled (async_run_all), Success (async_health_check) Operation canceled (async_hello).
(Boost.Redis) Connection lost: Operation canceled
(Boost.Redis) run-all-op: resolve addresses 127.0.0.1:6379
(Boost.Redis) run-all-op: connected to endpoint 127.0.0.1:6379
(Boost.Redis) writer-op: 108 bytes written.
(Boost.Redis) reader-op: 276 bytes read.
(Boost.Redis) hello-op: Success
(Boost.Redis) hello-op: error/canceled. Exiting ...
(Boost.Redis) reader-op: Operation canceled
(Boost.Redis) reader-op: error. Exiting ...
(Boost.Redis) writer-op: canceled (3). Exiting ...
(Boost.Redis) run-op: Operation canceled (reader), Success (writer)

auto main(int argc, char * argv[]) -> int
{
   try {
      config cfg;

      //if (argc == 3) {
      //   cfg.addr.host = argv[1];
      //   cfg.addr.port = argv[2];
      //}

      cfg.addr.host = "127.0.0.1";
      cfg.addr.port = "6379";
      cfg.password = "my_psw";
      cfg.database_index = 2;

      asio::io_context ioc;
      asio::co_spawn(ioc, co_main(cfg), [](std::exception_ptr p) {
         if (p)
            std::rethrow_exception(p);
      });
      ioc.run();

   } catch (std::exception const& e) {
      std::cerr << "(main) " << e.what() << std::endl;
      return 1;
   }
}
auto main() -> int
{
   std::cout << "Requires coroutine support." << std::endl;
   return 0;
}

// Receives server pushes.
auto
receiver(std::shared_ptr<connection> conn) -> asio::awaitable<void>
{
   {
      request ping_req;
      ping_req.push("PING");
      boost::redis::response<std::string> ping_resp;
      co_await conn->async_exec(ping_req, ping_resp, boost::asio::use_awaitable);
      std::cout << "PING: " << std::get<0>(ping_resp).value() << std::endl;
      std::get<0>(ping_resp).value().clear();
   }

   {
      request ping_req;
      ping_req.push("PING");
      boost::redis::response<std::string> ping_resp;
      co_await conn->async_exec(ping_req, ping_resp, boost::asio::use_awaitable);
      std::cout << "PING2: " << std::get<0>(ping_resp).value() << std::endl;
      std::get<0>(ping_resp).value().clear();
   }

   request req;
   req.push("SUBSCRIBE", "channel");

   generic_response resp;
   conn->set_receive_response(resp);

   // Loop while reconnection is enabled
   while (conn->will_reconnect()) {

      // Reconnect to the channels.
      co_await conn->async_exec(req, ignore, asio::deferred);

      // Loop reading Redis pushs messages.
      for (error_code ec;;) {
         // First tries to read any buffered pushes.
         conn->receive(ec);
         if (ec == error::sync_receive_push_failed) {
            ec = {};
            co_await conn->async_receive(asio::redirect_error(asio::use_awaitable, ec));
         }

         if (ec)
            break; // Connection lost, break so we can reconnect to channels.

         std::cout
            << resp.value().at(1).value
            << " " << resp.value().at(2).value
            << " " << resp.value().at(3).value
            << std::endl;

         consume_one(resp);
      }
   }
}

auto co_main(config cfg) -> asio::awaitable<void>
{
   auto ex = co_await asio::this_coro::executor;
   auto conn = std::make_shared<connection>(ex);
   asio::co_spawn(ex, receiver(conn), asio::detached);
   conn->async_run(cfg, {boost::redis::logger::level::debug}, asio::consign(asio::detached, conn));

   signal_set sig_set(ex, SIGINT, SIGTERM);
   co_await sig_set.async_wait();

   conn->cancel();
}

Thank you very much,
Rodrigo.

I noted that if I remove the need of password to connect (requirepass my_psw), everything works. I'm not using username on the configuration, btw.

Then please set default as username (in addition to the other fields)

 cfg.username = "default";

Let me know if it works.

Yep, it does work!

Thanks again.