skip to Main Content

In Azure Redis Enterprise with TimeSeries module enabled I have the following time series with 4 values of 1 at the key "key1":

$ TS.RANGE key1 - +

1) 1) (integer) 1693381431951
   2) 1
2) 1) (integer) 1693381435201
   2) 1
3) 1) (integer) 1693381436720
   2) 1
4) 1) (integer) 1693381438037
   2) 1

I am able to retrieve the same results by a Lua script:

$ EVAL "return redis.call('TS.RANGE', KEYS[1], '-', '+')" 1 key1

1) 1) (integer) 1693381431951
   2) 1
2) 1) (integer) 1693381435201
   2) 1
3) 1) (integer) 1693381436720
   2) 1
4) 1) (integer) 1693381438037
   2) 1

My question is how to sum up the values (and to get a 4 as result in the shown case)?

I am trying the following Lua code:

$ EVAL "local sum = 0; for stamp, val in redis.call('TS.RANGE', KEYS[1], '-', '+') do sum = sum + val end; return sum" 1 key1

(error) ERR Error running script (call to f_28243bc2b451f1770c76c1d7fcce23e7285f3baa): @user_script:1: user_script:1: attempt to call a table value

Doesn’t TS.RANGE return pairs of timestamp and numeric values?

My background is that I am calling ScriptEvaluateAsync() in my C# application and would like to sum up all values in a time series, when given a key.

2

Answers


  1. Chosen as BEST ANSWER

    By looking at the cjson.encode(tsrange) I have come up with the following solution, which I also would like to share:

    EVAL "local sum = 0; local tsrange = redis.call('TS.RANGE', KEYS[1], '-', '+'); for i = 1, #tsrange do sum = sum + tsrange[i][2]['ok'] end; return sum" 1 key1

    And then I call it in my C# app as:

    private const string LUA_SCRIPT = @"local sum = 0; local tsrange = redis.call('TS.RANGE', KEYS[1], '-', '+'); for i = 1, #tsrange do sum = sum + tsrange[i][2]['ok'] end; return sum";
    
    private static async Task<int> CalcSumAsync(IDatabase db, string key)
    {
        RedisResult result = await db.ScriptEvaluateAsync(LUA_SCRIPT, new RedisKey[] { key });
        return result.Type == ResultType.Integer ? (int)result : 0;
    }
    

  2. this can get a bit messy and the Redis Lua debugger can help (https://redis.io/docs/interact/programmability/lua-debugging/).

    Here’s a solution that will do what you need but the Lua could possibly be optimized – I’m not a Lua expert 🙂

    local sum = 0; 
    local rangeResponse = redis.call('ts.range', KEYS[1], '-', '+')
    for _, item in pairs(rangeResponse) do
      local val = tonumber(item[2]["ok"])
      sum = sum + val
    end
    return sum
    

    This unpacks the TS.RANGE response and sums the values. Let’s set up some data to use it:

    127.0.0.1:6379> 4 ts.add key1 * 1
    (integer) 1693393058028
    (integer) 1693393058034
    (integer) 1693393058035
    (integer) 1693393058036
    

    and check what we have:

    127.0.0.1:6379> TS.RANGE key1 - +
    1) 1) (integer) 1693393058028
       2) 1
    2) 1) (integer) 1693393058034
       2) 1
    3) 1) (integer) 1693393058035
       2) 1
    4) 1) (integer) 1693393058036
       2) 1
    

    Now let’s store the script in the redis server (I’ve put the script in a file called sumts.lua:

    $ redis-cli -x script load < ~/Desktop/sumts.
    lua
    "caf0749de1e79a8955e212a2fb0ea34e6c76a28c"
    

    We get the SHA for the script back and can use that to call it:

    127.0.0.1:6379> evalsha caf0749de1e79a8955e212a2fb0ea34e6c76a28c 1 key1
    (integer) 4
    

    we get the integer response 4, the sum of the values in the time series.

    Here’s an example of how to run through this script in the Lua debugger and see local variables:

    $ redis-cli --ldb --eval ~/Desktop/sumts.lua key1
    

    then…

    Lua debugging session started, please use:
    quit    -- End the session.
    restart -- Restart the script in debug mode again.
    help    -- Show Lua script debugging commands.
    
    * Stopped at 1, stop reason = step over
    -> 1   local sum = 0;
    lua debugger> n
    * Stopped at 2, stop reason = step over
    -> 2   local rangeResponse = redis.call('ts.range', KEYS[1], '-', '+')
    lua debugger> n
    <redis> ts.range key1 - +
    <reply> [[1693393058028,"+1"],[1693393058034,"+1"],[1693393058035,"+1"],[1693393058036,"+1"]]
    * Stopped at 3, stop reason = step over
    -> 3   for _, item in pairs(rangeResponse) do
    lua debugger> n
    * Stopped at 4, stop reason = step over
    -> 4     local val = tonumber(item[2]["ok"])
    lua debugger> n
    * Stopped at 5, stop reason = step over
    -> 5     sum = sum + val
    lua debugger> print
    <value> sum = 0
    <value> rangeResponse = {{1.69339e+12; {["ok"]="1"}}; {1.69339e+12; {["ok"]="1"}}; {1.69339e+12; {["ok"]="1"}}; {1.69339e+12; {["ok"]="1"}}}
    <value> (for generator) = "function@0x562d34cb1590"
    <value> (for state) = {{1.69339e+12; {["ok"]="1"}}; {1.69339e+12; {["ok"]="1"}}; {1.69339e+12; {["ok"]="1"}}; {1.69339e+12; {["ok"]="1"}}}
    <value> (for control) = 1
    <value> _ = 1
    <value> item = {1.69339e+12; {["ok"]="1"}}
    <value> val = 1
    

    You can keep using n to step through the loop.

    Hope this helps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search