lua-resty-redis
lua-resty-redis copied to clipboard
Lost number precision when saving to redis
Hi!
I've found a vary bad thing... as i have investigated, lua-resty-redis loose 64bit number precision, when saving numbers to redis.
See examples below. Lets assume, we have a number 1824192940134586
REDIS CLI CODE
127.0.0.1:6379[5]> set BIG_NUM_AS_NUM_REDIS 1824192940134586
OK
127.0.0.1:6379[5]> get BIG_NUM_AS_NUM_REDIS
"1824192940134586"
Everything works as expected
Now lets work with number through lua-resty-redis
NGINX LUA CODE
-- first, lets save a number in redis
redis:set("BIG_NUM_AS_NUM", 1824192940134586)
redis:set("BIG_NUM_AS_STR", string.format("%16.0f",1824192940134586))
-- now lets get keys
ngx.log(ngx.ERR, "BIG_NUM_AS_NUM: ", redis:get("BIG_NUM_AS_NUM"))
-- prints: 1.8241929401346e+15
ngx.log(ngx.ERR, "BIG_NUM_AS_NUM(tostr): ", string.format("%16.0f",redis:get("BIG_NUM_AS_NUM")))
-- prints: 1824192940134600 - FAIL
ngx.log(ngx.ERR, "BIG_NUM_AS_STR: ", redis:get("BIG_NUM_AS_STR"))
-- prints: 1824192940134586 - OK
ngx.log(ngx.ERR, "BIG_NUM_AS_NUM_REDIS: ", redis:get("BIG_NUM_AS_NUM_REDIS"))
-- prints: 1824192940134586 - OK
ngx.log(ngx.ERR, "BIG_NUM_AS_NUM_REDIS(tostr): ", string.format("%16.0f",redis:get("BIG_NUM_AS_NUM_REDIS")))
-- prints: 1824192940134586 - OK
What do you think about this solution: in function _gen_req(args) process numbers with string.format instead of tostring?
if type(arg) == "number" then
arg = string.format("%18.0f",arg)
elseif type(arg) ~= "string" then
arg = tostring(arg)
end
Of course it not a full solution. just an idea, because there are may different type of numbers
Interesting, it seems that the format result of tostring is awful sometimes:
[3] lua(main)> tonumber(tostring(1824192940134586)) == 1824192940134586
=> false
[4] lua(main)> tonumber(tostring(1824192940134586)) == 1824192940134600
=> true
I guess the tostring(num) in x64 is equal to string.format("%.14g", num).
See https://github.com/openresty/luajit2/blob/v2.1-agentzh/src/lj_strfmt_num.c#L589
My updated proposal for a check:
local arg = args[i]
if type(arg) ~= "string" then
if type(arg) == "number" and arg == math.floor(arg) then
arg = string.format("%.0f",arg)
else
arg = tostring(arg)
end
end
@KSDaemon In your redis-cli example, you are using a string literal value instead of a number value. So why don't you just use a string value in Lua too? Lua's native numbers do not support 64-bit precision anyway.