userver icon indicating copy to clipboard operation
userver copied to clipboard

Default ev ThreadPool size in conjunction with `ThreadPool::NextThread` is dangerous if not straight out broken

Open itrofimow opened this issue 2 years ago • 4 comments

https://github.com/userver-framework/userver/blob/9c371d29fa688f79176eea5faf01721530f84ef3/core/src/engine/ev/thread_pool.cpp#L51-L55

So this function just takes next thread % 2 (where 2 is the default size), seems alright.

Now, engine::io::Socket creates 2 watchers on construction, one for read direction and one for write direction. If the socket is a server connection socket, it's likely to operate way more heavily on a read direction than on the write one: every new request is 2 syscalls in libev (remove from epoll on wakeup, add into epoll on WaitReadable), when writing response through the socket might very well don't even touch libev.

Since there is just a single LISTEN socket in a server and accept-ed socket are created sequentially, for every socket its read watcher goes into ev-thread 0, and write watcher goes into ev-thread 1 (well, until something interfered with the order in ThreadPool), which leads to this:

image

itrofimow avatar Dec 21 '22 07:12 itrofimow

We can fix that by using utils::RandRange. It also gets rid of a shared counter, which are notorious for creating contention.

Anton3 avatar Jan 24 '23 22:01 Anton3

That's one way to solve this. Maybe mt19937 is an overkill here and some lighter PRNG would be better

itrofimow avatar Jan 31 '23 19:01 itrofimow

Actually it seems not only default ev ThreadPool size is broken. In our observation only half of ev threads are working. The other half is 0.0%.

image

dyumin avatar Mar 04 '24 18:03 dyumin

Could you try with an odd number of ev-threads? - I think right now some setups don't play nicely with an even number of ev-threads (which we should fix somehow)

itrofimow avatar Mar 05 '24 04:03 itrofimow