canonical / dqlite

Embeddable, replicated and fault-tolerant SQL engine.
https://dqlite.io
Other
3.8k stars 214 forks source link

Using uv_fs APIs when io_uring is available #582

Open cole-miller opened 1 year ago

cole-miller commented 1 year ago

Just opening an issue so that we have a place to discuss this. The libuv maintainers recently merged support for using io_uring to implement uv_fs APIs on sufficiently recent Linux kernels (the first release with this code is v1.45.0). It would be great to replace our hand-rolled async file I/O code with some simple calls to uv_fs, but there are some things to think about:

As I see it, we can either use uvfs* unconditionally and live with the consequences on old libuv/Linux, or to keep some handwritten fallback code around that does something other than blocking syscalls on the thread pool. I suspect the first option is not acceptable, and so the question becomes: what will the fallback code look like?

What do people think?

freeekanayaka commented 1 year ago

Just opening an issue so that we have a place to discuss this. The libuv maintainers recently merged support for using io_uring to implement uv_fs APIs on sufficiently recent Linux kernels (the first release with this code is v1.45.0). It would be great to replace our hand-rolled async file I/O code with some simple calls to uv_fs, but there are some things to think about:

  • We probably want to support libuv versions that predate the io_uring implementations
  • Even if we didn't, we definitely want to support Linux kernels that don't have io_uring

As I see it, we can either use uvfs* unconditionally and live with the consequences on old libuv/Linux, or to keep some handwritten fallback code around that does something other than blocking syscalls on the thread pool. I suspect the first option is not acceptable, and so the question becomes: what will the fallback code look like?

  • We could keep all our or current code, so the decision tree looks like: if io_uring, then use uv_fs; else if RWF_NOWAIT, then use that implementation; else, run it on the thread pool.

Since we already have the implementation and performance-wise it's going to be better than libuv's threadpool native fallback, I'd say let's keep it around for a while and once we don't need to support anymore older kernels/libuv we can get rid of it.

  • We could have a simpler fallback, perhaps without KAIO in the mix.

What would that simpler fallback look like? It feels that simplest fallback would be libuv's native fallback itself (threadpool), which we would get for free, and performance wise that's the max we could get because the only async alternative to io_uring on linux is KAIO.

cole-miller commented 1 year ago

What would that simpler fallback look like?

I'm not sure either, that's why I didn't elaborate :). No objection to keeping the KAIO around.

freeekanayaka commented 1 year ago

Yeah, I can't think of a simpler alternative that does not degrade performance. Also considering that the code we have in place seems kind of stable and is not creating a lot of maintenance burden. Keeping it around for another while should be cheap.

Probably the only place we'd need to modify in order to add libuv+io_ring support would be src/uv_writer.c. The src/uv_writer.h interface would remain the same, so the rest of the code will work unmodified, but internally in UvWriter we'd have a new code path using libuv+io_uring.