Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

second call to async_exec hangs and never returns #182

Closed
rotrida opened this issue Jan 26, 2024 · 4 comments · Fixed by #184
Closed

second call to async_exec hangs and never returns #182

rotrida opened this issue Jan 26, 2024 · 4 comments · Fixed by #184

Comments

@rotrida
Copy link

rotrida commented Jan 26, 2024

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();

@mzimbres
Copy link
Collaborator

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.

@rotrida
Copy link
Author

rotrida commented Jan 29, 2024

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.

@mzimbres
Copy link
Collaborator

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.

@rotrida
Copy link
Author

rotrida commented Jan 30, 2024

Yep, it does work!

Thanks again.

@mzimbres mzimbres linked a pull request Feb 11, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants