Server architecture: http & db backend with sync/async hand-off

Hi everyone,

I’m writing a server application and thought I’d seek some input from experienced Rust developers on the architecture. I’ve been writing complex servers in Kotlin, Java and previously C++ for years, but am fairly new to Rust.

The overall requirements are:

  • Accept http requests (think crud)
  • Data is provided by two database backends, both with a configurable number of external connections
  • Configuration is read at startup, but can also be updated at runtime
  • Some caching layers will be needed later on

My initial idea on how to build this:

  • Base http server part on hyper (https://github.com/hyperium/hyper). Requests are on a standard format, so no complex routing necessary
  • Launch one thread per DB connection
  • Put http requests onto one of two async channels - one for each DB type: (https://docs.rs/async-channel/1.4.1/async_channel/)
  • Each request allocates a response channel and includes that in the request
  • DB connection threads polls the associated request channel, performs a DB lookup & data transformation and puts the result on the request-specific response channel

Questions:

  1. Using channels this way as a hand-off between threads (and async/sync) - is that current best practice?
  2. It seems a bit wasteful to allocate a new response channel for every request. Any better way to do this?
  3. One of the two DB layers is Postgres, which has an async Rust client (https://docs.rs/tokio-postgres/0.5.5/tokio_postgres/). Perhaps it’s possible to create another design using this. However, I am very concerned about controlling number of DB connections.
  4. Should I use skip the async bit and build a thread-based server? It would probably work initially, but I am expecting this server to eventually handle a very large number of requests. There will be horizontal scaling as well of course though.

Any help very much appreciated! Thanks!

Sending database requests over a channel is a perfectly fine way to handle that. Creating channels is generally very cheap, and creating a new oneshot channel for every database request is probably fine.

Regarding your configuration, the typical way to handle that is a Tokio watch channel.