Waiting without blocking
A worker of nginx serves many connections at once from one event loop. A
handler that waits in a blocking call, such as sleep or a socket read,
stops the whole worker. Nginx::Async.sleep and
Nginx::Async::HTTP.sub_request wait through nginx instead: the Ruby code
of the handler runs in a fiber, which yields to nginx and is resumed from
nginx’s event loop.
An access handler's code calls
Nginx::Async.sleep(500) in its fiber__sleep adds a timer of 500 ms to nginx's event loop, and Fiber.yield suspends the fiberThe handler returns
NGX_DONE: nginx keeps the request and leaves the handlerThe worker serves its other connections while the timer runs
The timer fires:
ngx_mrb_timer_handler resumes the fiberThe code goes on after
sleep; when it ends, the request goes on with the next handler, or nginx ends it with the handler's resultNginx::Async.sleep, from the call until the handler's code goes on.Drawn from these lines of ngx_mruby at 0c72e3970 (2026-10-19):mrbgems/ngx_mruby_mrblib/mrblib/mrb_nginx.rblines 100 to 106src/http/ngx_http_mruby_async.clines 187 to 239, 241 to 314, 316 to 348 and 363 to 410src/http/ngx_http_mruby_module.clines 1061 to 1074src/http/ngx_http_mruby_core.hlines 30 to 35
- ngx_mruby runs the code of each handler in a fiber of mruby
(
ngx_mrb_start_fiber).Nginx::Async.sleeparms an nginx timer and yields the fiber; while it is suspended, the handler has returnedNGX_DONE, and the worker serves other connections. - When the timer fires,
ngx_mrb_timer_handlerresumes the fiber with the request that it belongs to. If the code ends, ngx_mruby gives nginx the result of the handler: the next handler of the phase runs forNginx::DECLINED, and nginx ends the request with any other result. Nginx::Async::HTTP.sub_requestworks in the same way: it starts an nginx subrequest and yields, and nginx resumes the fiber when the subrequest has finished.mruby_setand the post-read, server rewrite, rewrite and access handlers can wait and resume. A suspended handler returnsNGX_DONE, orNGX_AGAINwhile a subrequest continues.- Since the 2.7.3 changes, also integrated into 3.x, nginx keeps a normal
content handler’s request while
Nginx::Async.sleepwaits. The timer resumes the fiber; its response or returned status completes the request. Up to released 2.7.2, the request ended at the sleep without a response. A content handler reached byNginx.redirectfrom Ruby still has that limitation: the caller continues and can answer the request itself. - A content handler cannot call
Nginx::Async::HTTP.sub_requestafter it has waited inNginx::Async.sleep; this raisesRuntimeError. Inmruby_set, a subrequest does not hold the request: nginx continues to the content phase without waiting for the variable. In header/body filters and the log handler, both async methods raiseRuntimeError. See the 2.7.3 behavior changes and Nginx::Async .#sleep - Code that waits in
sleepof mruby-sleep, a socket of mruby-socket, a file, a shell command or a Redis call blocks the worker for that time (Trust boundary).
Nginx::