Cancellation and timeouts

By default, running a request with async_exec will wait until a connection to the Redis server is established by async_run. This may take a very long time if the server is down.

For this reason, it is usually a good idea to set a timeout to async_exec operations using the asio::cancel_after completion token:

using namespace std::chrono_literals;

// Compose a request with a SET command
request req;
req.push("SET", "my_key", 42);

// If the request hasn't completed after 10 seconds, it will be cancelled
// and an exception will be thrown.
co_await conn.async_exec(req, ignore, asio::cancel_after(10s));

See our example on timeouts for a full code listing.

You can also use cancel_after with other completion styles, like callbacks and futures.

cancel_after works because async_exec supports the per-operation cancellation mechanism. This is used by Boost.Asio to implement features like cancel_after and parallel groups. All asynchronous operations in the library support this mechanism. Please consult the documentation for individual operations for more info.

Retrying idempotent requests

We mentioned that async_exec waits until the server is up before sending the request. But what happens if there is a communication error after sending the request, but before receiving a response?

In this situation there is no way to know if the request was processed by the server or not. By default, the library will consider the request as failed, and async_exec will complete with an asio::error::operation_aborted error code.

Some requests can be executed several times and result in the same outcome as executing them only once. We say that these requests are idempotent. The SET command is idempotent, while INCR is not.

If you know that a request object contains only idempotent commands, you can instruct Boost.Redis to retry the request on failure, even if the library is unsure about the server having processed the request or not. You can do so by setting cancel_if_unresponded in request::config to false:

// Compose a request
request req;
req.push("SET", "my_key", 42); // idempotent
req.get_config().cancel_if_unresponded = false; // Retry the request even if it was written but not responded

// Makes sure that the key is set, even in the presence of network errors.
// This may suspend for an unspecified period of time if the server is down.
co_await conn.async_exec(req, ignore);