0

With Azure Redis Cache (Enterprise E10 with TimeSeries module enabled) it is possible to retrieve all timestamps from a time series for the key "12345678:5" by calling:

$ TS.RANGE 12345678:5 - +
 1) 1) (integer) 1693407731665
    2) 1
 2) 1) (integer) 1693407731694
    2) 1
 3) 1) (integer) 1693407731745
    2) 1
 4) 1) (integer) 1693407731771
    2) 1
...
18) 1) (integer) 1693407732075
    2) 1
19) 1) (integer) 1693407732103
    2) 1
20) 1) (integer) 1693407732127
    2) 1

But I am only interested in the timestamps/values of the recent 30 minutes (1800000 milliseconds).

So I calculate the timestamps "start" and "now" by the Lua script:

$ EVAL "local time = redis.call('TIME'); local now = time[1] * 1000 + time[2] / 1000; local start = now - tonumber(ARGV[1]); return { tonumber(ARGV[1]), start, now }" 1 12345678:5 1800000
1) (integer) 1800000
2) (integer) 1693493878001
3) (integer) 1693495678001

And then I try to call TS.RANGE using the Lua script:

$ EVAL "local time = redis.call('TIME'); local now = time[1] * 1000 + time[2] / 1000; local start = now - tonumber(ARGV[1]); local tsrange = redis.call('TS.RANGE', KEYS[1], start, now); return tsrange" 1 12345678:5 1800000
(error) ERR Error running script (call to f_a99152dc66083038fe6d6b9b6acef33495ef1960): @user_script:1: ERR TSDB: wrong fromTimestamp

Please advise me, what am I doing wrong?

My real background is that I try to call TS.ADD and TS.RANGE in my C# web app (the auto-expiration does not really seem to work, so I cannot just call TS.RANGE key - +, but have to specify the fromTimestamp, toTimestamp arguments to TS.RANGE explicitly):

private static readonly TimeSpan RETENTION_TIME = TimeSpan.FromMinutes(30);
// The script returns 0 if there are 20 or more timestamps in the past 30 min (1800000 ms).
// Otherwise it adds a new timestamp to the time series at the key and returns 1.
private const string LUA_SCRIPT =
@"
local sum = 0
local time = redis.call('TIME')
-- the final timestamp for TS.RANGE command in milliseconds
local now = time[1] * 1000 + time[2] / 1000
-- the start timestamp for TS.RANGE command in milliseconds
local start = now - tonumber(ARGV[1])
local tsrange = redis.call('TS.RANGE', KEYS[1], start, now)

for i = 1, #tsrange do
    -- calculate a sum of all values stored in the time series, from start to now
    sum = sum + tsrange[i][2]['ok']

    -- if there are enough timestamps in the time period, then just return 0
    if (sum >= 20) then
        return 0
    end
end

-- otherwise add the current timestamp and the value 1 to the time series
redis.call('TS.ADD', KEYS[1], '*', 1, 'RETENTION', ARGV[1], 'ON_DUPLICATE', 'SUM', 'ENCODING', 'UNCOMPRESSED')
return 1
";

private static async Task<bool> AddTimestampAsync(IDatabase db, string key, long retentionTime)
{
    RedisResult result = await db.ScriptEvaluateAsync(LUA_SCRIPT, new RedisKey[] { key }, new RedisValue[] { retentionTime });
    // return true if the Lua script returns 1, otherwise false
    return result.Type == ResultType.Integer && (int)result == 1;
}

I hope that I do not have to workaround my issue by modifying the for-loop above and comparing each timestamp one by one...

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416

1 Answers1

1

there's a couple of things going on here...

Here's a Lua script that successfully calls TS.RANGE with your calculated timestamps:

local t = redis.call('TIME');
local now = tonumber(t[1]) * 1000 + tonumber(t[2]) / 1000; 
local start = now - tonumber(ARGV[1]); 
local tsrange = redis.call('TS.RANGE', KEYS[1], math.floor(start), math.floor(now)); 
return tsrange

The changes I made to this to get it work are:

  • The response from TIME is a couple of string values, so changed them to numbers.
  • Your calculated timestamps had a fractional part to them so I used math.floor to get decimal millisecond values back.

Example debugging session for this script:

$ redis-cli --ldb --eval ~/Desktop/tsr.lua myts , 1800000
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 t = redis.call('TIME');
lua debugger> n
<redis> TIME
<reply> ["1693517015","71224"]
* Stopped at 2, stop reason = step over
-> 2   local now = tonumber(t[1]) * 1000 + tonumber(t[2]) / 1000;
lua debugger> n
* Stopped at 3, stop reason = step over
-> 3   local start = now - tonumber(ARGV[1]);
lua debugger> n
* Stopped at 4, stop reason = step over
-> 4   local tsrange = redis.call('TS.RANGE', KEYS[1], math.floor(start), math.floor(now));
lua debugger> n
<redis> TS.RANGE myts 1693515215071 1693517015071
<reply> [[1693516333573,"+1"],[1693516336087,"+2"],[1693516337642,"+3"],[1693516339302,"+4"],[1693516341365,"+5"],[1693516343262,"+6"]]
* Stopped at 5, stop reason = step over
-> 5   return tsrange
lua debugger> n

1) 1) (integer) 1693516333573
   2) 1
2) 1) (integer) 1693516336087
   2) 2
3) 1) (integer) 1693516337642
   2) 3
4) 1) (integer) 1693516339302
   2) 4
5) 1) (integer) 1693516341365
   2) 5
6) 1) (integer) 1693516343262
   2) 6

(Lua debugging session ended -- dataset changes rolled back)
Simon Prickett
  • 3,838
  • 1
  • 13
  • 26
  • Simon, thank you for the great support you are providing for Redis questions! Your suggestion has resolved my problem and I have also [reported this bug at RedisTimeSeries GitHub](https://github.com/RedisTimeSeries/RedisTimeSeries/issues/1509). – Alexander Farber Sep 02 '23 at 14:37