3

I would like to run specific long-running functions (which execute database queries) on a separate thread. However, let's assume that the underlying database engine only allows one connection at a time and the connection struct isn't Sync (I think at least the latter is true for diesel).

My solution would be to have a single separate thread (as opposed to a thread pool) where all the database-work happens and which runs as long as the main thread is alive. I think I know how I would have to do this with passing messages over channels, but that requires quite some boilerplate code (e.g. explicitly sending the function arguments over the channel etc.).

Is there a more direct way of achieving something like this with rust (and possibly tokio and the new async/await notation that is in nightly)?

I'm hoping to do something along the lines of:

let handle = spawn_thread_with_runtime(...);

let future = run_on_thread!(handle, query_function, argument1, argument2);

where query_function would be a function that immediately returns a future and does the work on the other thread.

Rust nightly and external crates / macros would be ok.

mrspl
  • 481
  • 1
  • 5
  • 10

1 Answers1

1

If external crates are an option, I'd consider taking a look at actix, an Actor Framework for Rust.

This will let you spawn an Actor in a separate thread that effectively owns the connection to the DB. It can then listen for messages, execute work/queries based on those messages, and return either sync results or futures.

It takes care of most of the boilerplate for message passing, spawning, etc. at a higher level.

There's also a Diesel example in the actix documentation, which sounds quite close to the use case you had in mind.

Dave Challis
  • 3,525
  • 2
  • 37
  • 65
  • Thanks for the tip. actix seems to be useful here, although I had hoped for an even more transparent way of achieving this. Maybe I'll look into writing some macros for this, but for now this seems sufficient. – mrspl Nov 13 '18 at 12:22
  • The other approach I've taken (though not tried with Diesel) is to use [r2d2](https://docs.rs/r2d2/0.8.3/r2d2/) and futures for this. I'd create a new future with the r2d2 pool handle, which delegates getting/returning connections to r2d2, so no message passing/locking etc. needed, even with a single connection and multiple futures. – Dave Challis Nov 13 '18 at 15:06
  • So you essentially limit the pool size to 1 in r2d2? – mrspl Nov 13 '18 at 18:38
  • @mrspl yup, if that's a requirement, though looking at the Diesel r2d2 module (which I'm guessing is just a re-export of r2d2), they have a test case with a pool size of 2: https://docs.diesel.rs/src/diesel/r2d2.rs.html#165 – Dave Challis Nov 13 '18 at 19:12