2
0
mirror of https://github.com/hibiken/asynq.git synced 2026-04-29 04:35:52 +08:00

Fix nil guard for MEMORY USAGE in memoryUsageCmd Lua script

MEMORY USAGE returns nil for keys that no longer exist (e.g., expired
or deleted task keys). In Lua, nil is converted to false (a boolean).
The script then attempts arithmetic on this boolean value, causing:

  ERR user_script:30: attempt to perform arithmetic on local 'bytes'
  (a boolean value)

This breaks the /api/queues endpoint in asynqmon, showing "Could not
retrieve queues live data" in the UI.

The fix adds nil guards around all three MEMORY USAGE calls on task
keys, and a divide-by-zero guard on agg_task_sample_size.

Tested in production with Redis 7.2 and asynq v0.25.1 worker.

Fixes #728
Related to #901
This commit is contained in:
Nil
2026-02-07 15:13:58 +08:00
parent d704b68a42
commit 2fd155e31d

View File

@@ -264,7 +264,9 @@ for i=1,2 do
if (table.getn(ids) > 0) then if (table.getn(ids) > 0) then
for _, id in ipairs(ids) do for _, id in ipairs(ids) do
local bytes = redis.call("MEMORY", "USAGE", ARGV[1] .. id) local bytes = redis.call("MEMORY", "USAGE", ARGV[1] .. id)
sample_total = sample_total + bytes if bytes then
sample_total = sample_total + bytes
end
end end
local n = redis.call("LLEN", KEYS[i]) local n = redis.call("LLEN", KEYS[i])
local avg = sample_total / table.getn(ids) local avg = sample_total / table.getn(ids)
@@ -281,7 +283,9 @@ for i=3,6 do
if (table.getn(ids) > 0) then if (table.getn(ids) > 0) then
for _, id in ipairs(ids) do for _, id in ipairs(ids) do
local bytes = redis.call("MEMORY", "USAGE", ARGV[1] .. id) local bytes = redis.call("MEMORY", "USAGE", ARGV[1] .. id)
sample_total = sample_total + bytes if bytes then
sample_total = sample_total + bytes
end
end end
local n = redis.call("ZCARD", KEYS[i]) local n = redis.call("ZCARD", KEYS[i])
local avg = sample_total / table.getn(ids) local avg = sample_total / table.getn(ids)
@@ -304,13 +308,17 @@ if table.getn(groups) > 0 then
local ids = redis.call("ZRANGE", group_key, 0, sample_size - 1) local ids = redis.call("ZRANGE", group_key, 0, sample_size - 1)
for _, id in ipairs(ids) do for _, id in ipairs(ids) do
local bytes = redis.call("MEMORY", "USAGE", ARGV[1] .. id) local bytes = redis.call("MEMORY", "USAGE", ARGV[1] .. id)
agg_task_sample_total = agg_task_sample_total + bytes if bytes then
agg_task_sample_size = agg_task_sample_size + 1 agg_task_sample_total = agg_task_sample_total + bytes
agg_task_sample_size = agg_task_sample_size + 1
end
end end
end end
end end
local avg = agg_task_sample_total / agg_task_sample_size if agg_task_sample_size > 0 then
memusg = memusg + (avg * agg_task_count) local avg = agg_task_sample_total / agg_task_sample_size
memusg = memusg + (avg * agg_task_count)
end
end end
return memusg return memusg
`) `)