From 5358c7a9f632ff524421c8482e4bbc8ded3d9bc0 Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Fri, 31 Jul 2026 23:41:51 +0300 Subject: [PATCH] drivers: fix task state reported to on_task_change on ttl In the "ttl tasks" loop of the fifottl and utubettl driver fibers the loop variable shadowed the 'state' module: for _, state in pairs(ttl_states) do ... task = self:delete(task[i_id]):transform(2, 1, state.DONE) Inside the loop `state` is a status string, so `state.DONE` indexes a string, evaluates to nil, and `transform(2, 1, nil)` sets the status field to NULL instead of DONE. The task passed to the user's on_task_change callback with the 'ttl' event was therefore malformed. Rename the loop variable to `task_state`. Closes #255 --- CHANGELOG.md | 3 ++ queue/abstract/driver/fifottl.lua | 6 +-- queue/abstract/driver/utubettl.lua | 6 +-- t/250-otc-cb-ttl.t | 62 ++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 t/250-otc-cb-ttl.t diff --git a/CHANGELOG.md b/CHANGELOG.md index 50e89c1..451da0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed +- The `on_task_change` callback got an expired task with a `NULL` status + instead of `DONE` (`fifottl`, `utubettl`) (#255). + ## [1.5.0] - 2026-07-10 This release introduces tube-level grants for roles (supported by all default diff --git a/queue/abstract/driver/fifottl.lua b/queue/abstract/driver/fifottl.lua index 8d07bc5..f9ba1db 100644 --- a/queue/abstract/driver/fifottl.lua +++ b/queue/abstract/driver/fifottl.lua @@ -109,9 +109,9 @@ local function fifottl_fiber_iteration(self, processed) end -- ttl tasks - for _, state in pairs(ttl_states) do - task = self.space.index.watch:min{ state } - if task ~= nil and task[i_status] == state then + for _, task_state in pairs(ttl_states) do + task = self.space.index.watch:min{ task_state } + if task ~= nil and task[i_status] == task_state then if now >= task[i_next_event] then task = self:delete(task[i_id]):transform(2, 1, state.DONE) self:on_task_change(task, 'ttl') diff --git a/queue/abstract/driver/utubettl.lua b/queue/abstract/driver/utubettl.lua index 0fd1c2b..4d73ecc 100644 --- a/queue/abstract/driver/utubettl.lua +++ b/queue/abstract/driver/utubettl.lua @@ -236,9 +236,9 @@ local function utubettl_fiber_iteration(self, processed) end -- ttl tasks - for _, state in pairs(ttl_states) do - task = self.space.index.watch:min{ state } - if task ~= nil and task[i_status] == state then + for _, task_state in pairs(ttl_states) do + task = self.space.index.watch:min{ task_state } + if task ~= nil and task[i_status] == task_state then if now >= task[i_next_event] then task = self:delete(task[i_id]):transform(2, 1, state.DONE) self:on_task_change(task, 'ttl') diff --git a/t/250-otc-cb-ttl.t b/t/250-otc-cb-ttl.t new file mode 100644 index 0000000..0124d28 --- /dev/null +++ b/t/250-otc-cb-ttl.t @@ -0,0 +1,62 @@ +#!/usr/bin/env tarantool +local test = require('tap').test() +test:plan(2) + +local fiber = require('fiber') + +local tnt = require('t.tnt') +tnt.cfg{} + +local engine = os.getenv('ENGINE') or 'memtx' + +local queue = require('queue') +local state = require('queue.abstract.state') + +-- In the "ttl tasks" loop of the driver fiber the loop variable shadowed the +-- 'state' module, so `state.DONE` was indexing a string and evaluated to nil. +-- As a result the task reported to the on_task_change callback had its status +-- field set to NULL instead of state.DONE. +local function check_ttl_status(test, tube_name, driver, put_opts) + local ttl_task + + local tube = queue.create_tube(tube_name, driver, { + engine = engine, + ttl = 0.1, + on_task_change = function(task, stats_data) + if stats_data == 'ttl' then + ttl_task = task + end + end + }) + + tube:put({'expired'}, put_opts) + + local deadline = fiber.clock() + 30 + while ttl_task == nil and fiber.clock() < deadline do + fiber.sleep(0.05) + end + + if ttl_task == nil then + test:fail('on_task_change is called with the "ttl" event') + test:fail('the expired task is reported in the DONE state') + return + end + + test:ok(true, 'on_task_change is called with the "ttl" event') + test:is(ttl_task[2], state.DONE, + 'the expired task is reported in the DONE state') +end + +test:test('fifottl', function(test) + test:plan(2) + check_ttl_status(test, 'otc_ttl_fifottl', 'fifottl', {}) +end) + +test:test('utubettl', function(test) + test:plan(2) + check_ttl_status(test, 'otc_ttl_utubettl', 'utubettl', {utube = 'u'}) +end) + +tnt.finish() +os.exit(test:check() and 0 or 1) +-- vim: set ft=lua :