StackExchange / StackExchange/StackExchange.Redis

EVALSHA isn't working within transaction.

Open
#2,531 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

⚙️ area:lua
Dominant language
C#
Stars
6.2k
Forks
1.6k
Avg merge
1d 15h
Merged PRs (30d)
43

Description

I have create a LUA script to execute within a Transaction with other native command functions.

When I run the LUA script without transaction, it works as expected and EVALSHA get called always.
But when I wrap the same LUA script with same key & values and execute in a transaction (with or without other command functions), the framework sends script for every execution. EVAL gets called on script always.

Is this an expected behaviour or am I doing from wrong? @NickCraver @mgravell

Any suggessions to use EVALSHA in Transaction without sending script everytime?

References:-------
Below are MONITOR logs with NO Transaction:

1693207783.585817 [0 122.160.68.144:57986] "SELECT" "0"
1693207783.585817 [0 122.160.68.144:57986] "SCRIPT" "LOAD" "MY_LUA_SCRIPT"
1693207784.861812 [0 122.160.68.144:57986] "EVALSHA" "5092f92c0e62b72c3bf6539ab62a79fefc836395" "1" "{u:425}:pd" "{u:425}:pd" "$.logdt" "\"28-08-2023 12:59:45\""
1693207784.977812 [0 122.160.68.144:56816] "ping"
1693207786.137807 [0 122.160.68.144:57986] "EVALSHA" "5092f92c0e62b72c3bf6539ab62a79fefc836395" "1" "{u:425}:pd" "{u:425}:pd" "$.logdt" "\"28-08-2023 12:59:46\""
1693207788.525797 [0 122.160.68.144:57989] "PING"
1693207788.537797 [0 122.160.68.144:57988] "PING"

Below are MONITOR logs with Transaction:

1693207779.461834 [0 122.160.68.144:58003] "PING"
1693207779.757833 [0 122.160.68.144:57986] "MULTI"
1693207779.757833 [0 122.160.68.144:57986] "EVAL" "MY_LUA_SCRIPT"
1693207779.757833 [0 122.160.68.144:57986] "SELECT" "0"
1693207779.757833 [0 122.160.68.144:57986] "EXEC"
1693207780.473830 [0 122.160.68.144:58004] "PING"
1693207781.029828 [0 122.160.68.144:57986] "SELECT" "0"
1693207781.029828 [0 122.160.68.144:57986] "MULTI"
1693207781.029828 [0 122.160.68.144:57986] "EVAL" "MY_LUA_SCRIPT"
1693207781.029828 [0 122.160.68.144:57986] "SELECT" "0"
1693207781.029828 [0 122.160.68.144:57986] "EXEC"
1693207782.309823 [0 122.160.68.144:57986] "SELECT" "0"
1693207782.309823 [0 122.160.68.144:57986] "MULTI"
1693207782.309823 [0 122.160.68.144:57986] "EVAL" "MY_LUA_SCRIPT"
1693207782.309823 [0 122.160.68.144:57986] "SELECT" "0"
1693207782.309823 [0 122.160.68.144:57986] "EXEC"
1693207783.293819 [0 122.160.68.144:57970] "PING"

Extension function JsonSC_SetAsync:

public static async Task<bool> JsonSC_SetAsync(this ITransaction _db, RedisKey a_key, string a_path, object a_value, CommandFlags a_flags = CommandFlags.None)
{
	List<RedisValue> list = new List<RedisValue> { a_path, (RedisValue)JsonConvert.SerializeObject(a_value) };
	LuaScript l_sc = LuaScript.Prepare(SCRIPT__SET);
	RedisResult l_rr = (await _db.ScriptEvaluateAsync(l_sc, new { key = a_key, path = a_path, value = JsonConvert.SerializeObject(a_value) }));
	return l_rr.OKtoBoolean();
}

My test function for WITH Transaction:

async void TestMeWithTransaction()
{
	await Task.Delay(10000);
	Logger.Error("WithTransaction--START");
	RedisConnection l_conn = RedisHandlerCommands.Default.GetConnection();
	ITransaction l_trans = l_conn.Database.CreateTransaction();

	l_trans.JsonSC_SetAsync("{u:425}:pd", "$.logdt", DateTime.Now.ToString());
	bool l_isSuccess = await l_trans.ExecuteAsync();
	Logger.Error("IsSuccess: {l_isSuccess}", l_isSuccess);

	await Task.Delay(5000);
	l_trans.JsonSC_SetAsync("{u:425}:pd", "$.logdt", DateTime.Now.ToString());
	l_isSuccess = await l_trans.ExecuteAsync();
	Logger.Error("IsSuccess: {l_isSuccess}", l_isSuccess);


	await Task.Delay(5000);
	l_trans.JsonSC_SetAsync("{u:425}:pd", "$.logdt", DateTime.Now.ToString());
	l_isSuccess = await l_trans.ExecuteAsync();
	Logger.Error("IsSuccess: {l_isSuccess}", l_isSuccess);
	Logger.Error("WithTransaction--END");
	TestMeNoTransaction();
}

My test function for WITH NO Transaction:

async void TestMeNoTransaction()
{
	await Task.Delay(1000);
	Logger.Error("NoTransaction--START");
	RedisConnection l_conn = RedisHandlerCommands.Default.GetConnection();

	bool l_isSuccess = await l_conn.Database.JsonSC_SetAsync("{u:425}:pd", "$.logdt", DateTime.Now.ToString());			
	Logger.Error("IsSuccess: {l_isSuccess}", l_isSuccess);

	await Task.Delay(1000);
	l_isSuccess = await l_conn.Database.JsonSC_SetAsync("{u:425}:pd", "$.logdt", DateTime.Now.ToString());			
	Logger.Error("IsSuccess: {l_isSuccess}", l_isSuccess);

	await Task.Delay(1000);
	l_isSuccess = await l_conn.Database.JsonSC_SetAsync("{u:425}:pd", "$.logdt", DateTime.Now.ToString());			
	Logger.Error("IsSuccess: {l_isSuccess}", l_isSuccess);
	Logger.Error("NoTransaction--STARTEND");
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the JsonSC_SetAsync extension and the TestMeWithTransaction and TestMeNoTransaction functions shown in the issue; inspect how ITransaction.ScriptEvaluateAsync is queued and compare the MONITOR traces for EVAL versus EVALSHA. Done means establishing whether transaction execution should reuse the script SHA and, if a change is warranted, covering the observed behavior with a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, redis
Domain
database
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.