> I'm Ruben, the author of Boost.MySQL, and I want to present (and maybe get
> some feedback about) a project I'm developing to showcase how to do async
> stuff with Boost server-side.
...
So I've decided to [...] create a project that
> allows Boost authors to try new things in a realistic environment, while
> demonstrating to users how they can use Boost to build an app.
>
Very nice. From https://github.com/anarthal/servertech-chat/#architecture
in describing the example app:
The server is based on Boost.Beast, asynchronous (it uses stackful
> coroutines) and single-threaded.
This is a good/appropriate introduction for programmers to experience
developing what has the power of an asynchronous application but with the
simplicity of a single-threaded application without the need for locks.
A more advanced app, and what I would like to see personally, is an example
and architectural discussion on design patterns involving how best to
handle server requests that require more time/resources that may not be
appropriate for a single-threaded server (e.g. a database server.) From a
high-level perspective, my current thinking on this is:
- Handle fast requests in the main single-threaded boost.asio event loop
(assuming they don't access resources locked by the next bullet point.)
- Handle longer requests by delegating to a separate thread pool that
triggers a callback when done, without blocking the single-threaded event
loop. The threads in the separate thread pool do "traditional locking" via
mutexes, read/write locks, etc.
Are there more modern approaches/techniques?
In either case, a discussion about these issues would be valuable IMHO
after the introductory example.
Thank you,
Matt
> On Tue, Sep 5, 2023 at 12:43 PM Ruben Perez via Boost <
> bo...@lists.boost.org>
> > showcase how to do async stuff with Boost server-side.
>
> A more advanced app, and what I would like to see personally, is an example
> and architectural discussion on design patterns involving how best to
> handle server requests that require more time/resources that may not be
> appropriate for a single-threaded server (e.g. a database server.)
>
I agree. This is also my case.
While I can understand Ruben focuses on using async libs "all the way",
unfortunately I don't have that luxury (or honestly the knowledge). In fact,
some of the "protocols" / APIs I must support allow requesting large amount
of data, thus I don't want a single request from one client to block all
other clients.
When everything is async, including talking to remote RDBMS's, maybe that
one big request gets "suspended", allowing other (independent) requests to
make progress, but in my case with a non-async DB layer to talk to, I don't
think a single-threaded server is a viable solution.
So while your initiative is great, and I'll try to study it, getting into a
"less pure"
hybrid app example, mixing async *and* sync (typically talking to DBs that
did
not get your async Boost.MySQL or Boost.Redis treatment), as Matt mentioned,
would definitely help me. FWIW.
In any case, thank you Ruben for trying to teach the rest of us the "async
way" with Boost. --DD
> > A more advanced app, and what I would like to see personally, is an
> example and architectural discussion on design patterns involving how best
> to handle server requests that require more time/resources that may not be
> appropriate for a single-threaded server (e.g. a database server.) From a
> high-level perspective, my current thinking on this is:
>
> I guess you mean any protocol that does not have an async library, or a
> resource-intensive task such as image processing? If there is a specific
> task or protocol you'd like to see, please do mention it.
>
The example I have in mind is an actual database server (not client) which
uses Boost.Asio to handle SELECT, UPDATE, CREATE TABLE sql statements, etc.
The challenges posed by a database server are quite general and will apply
to many applications, and I would guess that any developer considering
using Boost.Asio for handling asynchronous I/O operations will have
questions along the lines of:
- How should I handle longer requests that are delegated to a thread
pool, especially with respect to resource contention? E.g. An UPDATE query
to table A should lock table A so that asynchronous SELECT queries on A
don't read inconsistent data.
A few ideas that come to mind are:
- Use traditional mutexes and lock guards
<https://en.cppreference.com/w/cpp/thread/lock_guard> for each table.
- Use unique locks <https://en.cppreference.com/w/cpp/thread/unique_lock>
on writes, and shared locks
<https://en.cppreference.com/w/cpp/thread/shared_lock> for reads.
- Use a locking manager to avoid deadlocks (if scoped lock
<https://en.cppreference.com/w/cpp/thread/scoped_lock> isn't sufficient.)
- A completely different paradigm: Multiversion Concurrency Control
<https://www.postgresql.org/docs/current/mvcc-intro.html> as PostgreSQL
uses, in which changes are done to a separate version of the data. (This is
quite a general concept and doesn't just apply to databases.) When
completed, the version upgrade can be done in the main Boost.Asio thread
and thus avoid locking (with caveats).
Are there more modern/sophisticated techniques to deal with these issues?
Or if they are outside the scope of Boost.Asio, it is still worth
mentioning them so that we know to deal with them at the right level in the
architecture.
These are the types of considerations/discussions I am interested in.