I've been working on the "timer" git branch for the ngx_lua project
where I've introduced a new timer API.
Essentially it is the ngx.timer.at() function on the Lua land, which
can be used in all contexts but init_by_lua*. The syntax of this
function is as follows:
local ok, err = ngx.timer.at(delay, user_callback, user_arg1,
where the first argument, "delay", specifies the delay for the timer,
in seconds. One can specify fractional seconds like 0.001 to mean 1
millisecond here. 0 delay can also be specified, in which case the
timer will immediately expire when the current handler yields
execution. The second argument to ngx.timer.at(), "user_callback", can
be any Lua function, which will be invoked later in a background
"light thread" after the delay specified. The user callback will be
called by ngx_lua with the following arguments:
premature, user_arg1, user_arg2, ...
where the "premature" argument takes a boolean value indicating
whether it is a premature timer expiration or not, and user_arg1,
user_arg2, and etc, are those (extra) user arguments specified when
calling ngx.timer.at() as the remaining arguments.
Premature timer expiration happens when the Nginx worker process is
trying to shut down, as in an Nginx configuration reload triggered by
the HUP signal or in an Nginx server shutdown. When the Nginx worker
is trying to shut down, one can no longer call ngx.timer.at() to
create new timers and in that case ngx.timer.at() will return nil and
a string, "process exiting", describing the error.
When a timer expires, the user Lua code in the timer callback is
running in a "light thread" detached completely from the original
request creating the timer. So objects with the same lifetime as the
request creating them, like cosockets, cannot be shared between the
original request and the timer user callback function.
Because timer callbacks run in the background and their running time
will not add to any client request's response time, they can easily
accumulate in the server and exhaust system resources due to either
Lua programming mistakes or too much client traffic. To prevent
extreme consequences like crashing the Nginx server, there are
built-in limitations on both the number of "pending timers" and the
number of "running timers" in an Nginx worker process. The "pending
timers" here mean timers that have not yet been expired and "running
timers" are those whose user callbacks are currently running.
By default, the maximal number of pending timers allowed in an Nginx
worker is 1024 while the maximal number of running timers is 256.
Furthermore, the user can configure the lua_max_pending_timers and
lua_max_running_timers directives in nginx.conf to adjust these two
According to the current implementation, each "running timer" will
take one (fake) connection record from the global connection record
list configured by the standard "worker_connections" directive in
nginx.conf. So ensure that the worker_connections directive is set to
a large enough value that takes into account both the real connections
and fake connections required by timer callbacks (as limited by the
A lot of ngx_lua's Lua APIs are enabled in the context of the timer
callbacks, like stream/datagram cosockets (ngx.socket.*), shared
memory dictionaries (ngx.shared.*), user coroutines (coroutine.*),
user "light threads" (ngx.thread.*), ngx.exit(), ngx.now()/ngx.time(),
ngx.md5()/ngx.sha1(), are all allowed. But the subrequest API (like
ngx.location.capture), the ngx.req.* API, the downstream output API
(like ngx.say, ngx.print, and ngx.flush) are explicitly disabled in
You can check out the (declarative) test cases in the ngx_lua test
suite for more concrete examples for the timer API:
And you're very welcome to try out the "timer" git branch in the
ngx_lua repository on Github:
Comments on the current design and implementation of this timer API
will be highly appreciated.