Event-driven I/O (epoll / kqueue / io_uring)
TechniqueNetworking and OSSeniorStaffUpdated
Readiness-based (epoll, kqueue) or completion-based (io_uring, IOCP) APIs that let one thread watch tens of thousands of file descriptors without a thread per connection.
When to Use Event-driven I/O (epoll / kqueue / io_uring)
Any server expecting more than ~10k concurrent connections. Thread-per-connection caps out at the thread count you can schedule; epoll scales to whatever your file descriptor (fd) limit allows.
In Practice
Nginx, Redis, Node.js (via libuv), and Envoy are all single-threaded event loops over epoll. io_uring (Linux 5.1+) goes further with submission/completion queues shared with the kernel, removing syscalls per op; ScyllaDB and recent Postgres patches use it.
How Event-driven I/O (epoll / kqueue / io_uring) Comes Up in Interviews
Scenario
Your Java service is supposed to handle 100k concurrent long-lived connections but dies around 10k. Threads are pegged and CPU is mostly in kernel time. What is the bottleneck?
Thread-per-connection model: each connection holds a thread, and at ~10k threads you are paying enormous context-switch and stack-memory cost. Move to an event-driven model (Netty on epoll, or virtual threads / Loom) so one thread can watch many sockets.
This is the C10k problem from 1999, still tripping people up. Each Java thread reserves ~1MB of stack by default; 10k threads is 10GB before you do anything. Event loops keep state in heap-allocated callbacks instead.