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