StackExchange / StackExchange.Redis

General purpose redis client
https://stackexchange.github.io/StackExchange.Redis/
Other
5.87k stars 1.51k forks source link

Lua redis() command arguments must be strings or integers #2535

Closed afarber closed 1 year ago

afarber commented 1 year ago

Good evening, I have a question please.

There is a command, which works perfectly at my Redis Azure Cache "redis-cli" prompt (an Enterprise E10 instance with the TimeSeries module enabled) - here I have called it 4 times in a row:

EVAL "redis.call('TS.ADD', KEYS[1], '*', ARGV[1], 'RETENTION', ARGV[2], 'ON_DUPLICATE', 'SUM', 'ENCODING', 'UNCOMPRESSED'); return redis.call('TS.RANGE', KEYS[1], '-', '+')" 1 key1 1 1800000
1) 1) (integer) 1693323424958
   2) 1
2) 1) (integer) 1693323485951
   2) 1
3) 1) (integer) 1693323528702
   2) 1
4) 1) (integer) 1693323531678
   2) 1

However, when I try to call the same command in a simple C# .Net console app:

private static readonly TimeSpan RETENTION_TIME = TimeSpan.FromMinutes(30);

private const string LUA_CMD = @"redis.call('TS.ADD', KEYS[1], '*', ARGV[1], 'RETENTION', ARGV[2], 'ON_DUPLICATE', 'SUM', 'ENCODING', 'UNCOMPRESSED'); return redis.call('TS.RANGE', KEYS[1], '-', '+')";

private static async Task<int> AddTimestampReturnSumAsync(IDatabase db, string key, int retentionTime)
{
    RedisResult result = await db.ScriptEvaluateAsync(LUA_CMD, new RedisKey[] { key }, new RedisValue[] { retentionTime });
    Console.WriteLine(JsonSerializer.Serialize(result));
    return 0; // a placeholder, I actually want to return a sum of the values
}

Then the following line:

int sum = await AddTimestampReturnSumAsync(redis.GetDatabase(), "key1", (int)RETENTION_TIME.TotalMilliseconds);

fails with:

StackExchange.Redis.RedisServerException: 'ERR Error running script (call to f_503fcb14af1e60051a54d3617d5265f753dfe423): @user_script:1: @user_script: 1: Lua redis() command arguments must be strings or integers'

image

Trying to debug the issue, I have tried the following 2 strings and they just work as expected:

private const string LUA_CMD = @"return KEYS[1]"; // returns a RedisResult with "key1"

private const string LUA_CMD = @"return ARGV[1]"; // returns a RedisResult with 1800000

I am just trying to call TS.ADD and the TS.RANGE as a single atomic unit from my C# app.

Currently, I am studing the ScriptingTests.cs trying to understand what is different with my code...

Thank you for any hints

slorello89 commented 1 year ago

You're only adding one argument to ARGV when your script is expecting two :)

try something like:

var muxer = ConnectionMultiplexer.Connect("localhost");
var db = muxer.GetDatabase();
TimeSpan RETENTION_TIME = TimeSpan.FromMinutes(30);

string LUA_CMD = @"redis.call('TS.ADD', KEYS[1], '*', ARGV[1], 'RETENTION', ARGV[2], 'ON_DUPLICATE', 'SUM', 'ENCODING', 'UNCOMPRESSED'); return redis.call('TS.RANGE', KEYS[1], '-', '+')";

RedisResult result = await db.ScriptEvaluateAsync(LUA_CMD, new RedisKey[] { "key1" }, new RedisValue[] { 1, Convert.ToInt64(RETENTION_TIME.TotalMilliseconds) });
Console.WriteLine(JsonSerializer.Serialize(result));
return 0; // a placeholder, I actually want to return a sum of the values
afarber commented 1 year ago

Thank you!