Boost logo

Boost :

From: Marcelo Zimbres Silva (mzimbres_at_[hidden])
Date: 2023-01-17 18:39:27


On Tue, 17 Jan 2023 at 14:38, Vinnie Falco via Boost
<boost_at_[hidden]> wrote:
>
> I have questions. Why is async_run needed?

It was introduced to coordinate reads on the socket

1. When a server-push is received, async_run gives IO control to
connection::async_receive so it can read the push from the socket.

2. When the response to a request arrives it gives IO control to the
next pending connection::async_exec.

3. Notice that in order to support pushes, we need a way to keep
reading from the socket, the question that still must be answered is
why we need both async_run and async_receive (see below).

async_run is also used to coordinate writes socket

4. async_run will also spawn an operation that will write pending
requests to the socket when it becomes possible. For example, you call
async_exec to execute a command but there is an ongoing write from a
previous call or we are waiting for a response to arrive and can't
write yet. The implementation will suspend the async_exec call until
the request can be written and its response has arrived.

> Why doesn't the library just keep an operation running in the
> background automatically for the user?

5. Because there wouldn't be a good way to communicate write errors to
the user, for example, if async_write fails async_run will complete
with the error reported by async_write.

6. Because the connection can be reconnected: async_run can be called
in a loop by the user to proceed with the execution of the pending
async_execs after a disconnection. This is meant to support quick and
efficient failover. Offering this functionality as a built-in feature
might bloat the class.

Some questions that might arise are

* Why do we need a connection that can be reconnected?

Because a connection that is resilient against failovers and
disconnections in general makes user code a lot simpler, you won't
have to implement retries on user side, see [3] for example.

Because it offers better performance: Imagine we have a chat server
with 100k Websocket connections and the admin wants to force a
failover to another Redis instance. Without this feature we would have
100k sessions throwing, catching and retrying, which is concerning
IMO.

* Why don't you merge async_run and async_receive in a single function.

That is possible but would make the code more complex AFAICS. The
separation between async_run and async_receive makes it possible to
have a clean reconnect loop like [1] and a clean loop that receives
pushes like [2].

* Why don't you trigger writes from async_exec instead of spawning a
write operation from async_run?

I found it harder to implement it like this, perhaps I am missing some
async primitives like those from asem library. At least, I think
there would be a performance hit, reasons we can discuss later.

[1] https://github.com/mzimbres/aedis/blob/c88fcfb9edbaca3cc90e761d8b3087c0eed4a9fb/examples/cpp20_subscriber.cpp#L58
[2] https://github.com/mzimbres/aedis/blob/c88fcfb9edbaca3cc90e761d8b3087c0eed4a9fb/examples/cpp20_subscriber.cpp#L40
[3] https://github.com/mzimbres/aedis/blob/c88fcfb9edbaca3cc90e761d8b3087c0eed4a9fb/examples/cpp20_echo_server.cpp#L21


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk