diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8a3312c0..bf9cd16f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,6 +57,7 @@ jobs: - "spec/01*.lua spec/04*.lua spec/05*.lua spec/06*.lua spec/07*.lua spec/08*.lua" - "spec/02*.lua" - "spec/03*.lua" + - "spec/09*.lua" openresty_version: - "1.19.9.1" @@ -116,7 +117,11 @@ jobs: - name: Tests run: | eval $(luarocks path) - resty -I lib -I spec spec/runner.lua --coverage --verbose -o htest --shuffle-tests ${{ matrix.busted_args }} + if [[ "${{ matrix.busted_args }}" == "spec/09*.lua" ]]; then + resty --shdict 'timer_jump_the_gun 1m' -I lib -I spec spec/runner.lua --coverage --verbose -o htest --shuffle-tests ${{ matrix.busted_args }} + else + resty -I lib -I spec spec/runner.lua --coverage --verbose -o htest --shuffle-tests ${{ matrix.busted_args }} + fi - name: Show coverage run: | diff --git a/lib/resty/timerng/job.lua b/lib/resty/timerng/job.lua index 96802772f..99e3c6559 100644 --- a/lib/resty/timerng/job.lua +++ b/lib/resty/timerng/job.lua @@ -241,13 +241,20 @@ function _M.new(wheels, name, callback, delay, once, debug, argc, argv) }, } + if once then + self.steps = self.steps + 1 + end + if debug then job_create_meta(self) end + local create_time = ngx_now() + self.create_time = create_time + if self.name == nil then self.name = string_format("unix_timestamp=%f;counter=%d:meta=%s", - math_floor(ngx_now() * 1000), + math_floor(create_time * 1000), NAME_COUNTER, self.meta.name) diff --git a/lib/resty/timerng/wheel/group.lua b/lib/resty/timerng/wheel/group.lua index 5b29bc153..06b55de18 100644 --- a/lib/resty/timerng/wheel/group.lua +++ b/lib/resty/timerng/wheel/group.lua @@ -1,3 +1,5 @@ +local math_floor = math.floor + local utils = require("resty.timerng.utils") local wheel = require("resty.timerng.wheel") local array = require("resty.timerng.array") diff --git a/spec/09-jump_the_gun_spec.lua b/spec/09-jump_the_gun_spec.lua new file mode 100644 index 000000000..545a2f3c0 --- /dev/null +++ b/spec/09-jump_the_gun_spec.lua @@ -0,0 +1,113 @@ + +local timer_module = require("resty.timerng") +local helper = require("helper") + +local sleep = ngx.sleep +local update_time = ngx.update_time +local now = ngx.now +local timer_running_count = ngx.timer.running_count +local string_format = string.format + + +local function callback_func(premature, create_time, delay) + if premature then + return + end + + update_time() + local now = now() + local dict = ngx.shared["timer_jump_the_gun"] + if not dict then + error("not found shared dict: timer_jump_the_gun") + return + end + + dict:set("not_jump_the_gun", now - delay > create_time) +end + + +local function every_func() + update_time() +end + +local rand_intervals = { + 0.111, + 0.222, + 0.333, + 0.444, + 0.555, + 0.666, + 0.777, + 0.888, + 0.999, +} + +for rand_interval in pairs(rand_intervals) do + insulate("timer jump the gun", function () + local timer = { } + + randomize() + + lazy_setup(function () + timer = timer_module.new({ + min_threads = 16, + max_threads = 32, + }) + + assert(timer:start()) + + end) + + lazy_teardown(function () + timer:freeze() + timer:destroy() + + helper.wait_until(function () + assert.same(1, timer_running_count()) + return true + end) + + end) + + after_each(function () + assert.has_no.errors(function () + timer:cancel(helper.TIMER_NAME_ONCE) + end) + end) + + it("test", function () + local delay = 30 * helper.RESOLUTION + local interval = 10 * helper.RESOLUTION + update_time() + + assert.has_no.errors(function () + assert( + timer:every(interval, every_func) + ) + end) + + sleep(rand_interval) + + update_time() + assert.has_no.errors(function () + local create_time = now() + assert( + timer:named_at("foo", delay, callback_func, create_time, delay) + ) + end) + + sleep(4) + + local dict = ngx.shared["timer_jump_the_gun"] + if not dict then + error("not found shared dict: timer_jump_the_gun") + return + end + + local jump_the_gun = dict:get("not_jump_the_gun") + assert.is_not_nil(jump_the_gun) + assert.is_true(jump_the_gun) + end) + end) +end +