Files
caliverse_server/ServerCore/Redis/RedisWithLuaScriptExecutorHelper.cs
2025-11-28 16:54:56 +09:00

890 lines
45 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Amazon.DynamoDBv2.DocumentModel;
using Renci.SshNet.Security;
using StackExchange.Redis;
namespace ServerCore;
// HANDOVER: RedisWithLuaScriptExecutorBase 참조하고, Redis에 저장하고자 하는 정보의 무결성을 보장해 주기위한 구조를 제공 한다.
public static class RedisWithLuaScriptExecutorHelper
{
//=============================================================================================
// 랭킹 맴버를 추가하고 최고 순위자를 반환하고 차순위자들을 제거하지 않는다. - kangms
//=============================================================================================
public static async Task<(bool, string, double, string, double, bool, int)> tryPlaceTopRank( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string rankKey, string memberId, double memberPoint
, string blockKey )
{
// Lua 스크립트 정의
var lua_script_name = "placeTopRank";
var lua_script = @"
local blockKey = KEYS[2]
local rankKey = KEYS[1]
local rankerId = ARGV[1]
local rankerPoint = tonumber(ARGV[2])
local blockKeyExists = redis.call('EXISTS', blockKey)
if blockKeyExists == 1 then
return { 1, '', 0, '', 0 }
end
redis.call('ZADD', rankKey, rankerPoint, rankerId)
local currentHighestRanker = redis.call('ZREVRANGE', rankKey, 0, 0, 'WITHSCORES')
local result = {}
local rankerTotalCount = redis.call('ZCARD', rankKey)
if currentHighestRanker[1] ~= nil then
local currentHighestPoint = tonumber(currentHighestRanker[2])
local currentHighestRankerId = currentHighestRanker[1]
if rankerPoint > currentHighestPoint then
rankerTotalCount = redis.call('ZCARD', rankKey)
result = { 0, rankerId, rankerPoint, currentHighestRankerId, currentHighestPoint, rankerTotalCount }
else
result = { 0, currentHighestRankerId, currentHighestPoint, '', 0, rankerTotalCount }
end
else
totalRankers = redis.call('ZCARD', rankKey)
result = { 0, rankerId, rankerPoint, '', 0, rankerTotalCount }
end
return result
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if(false == is_success)
{
return (false, string.Empty, 0, string.Empty, 0, false, 0);
}
bool is_blocked = false;
string curr_ranker_id = string.Empty;
double curr_ranker_point = 0;
string old_ranker_id = string.Empty;
double old_ranker_point = 0;
bool is_top_change = false;
int ranker_count = 0;
try
{
// 루아 스크립트 실행, KEYS[1]에 rankKey, ARGV[1]에 memberId, ARGV[2]에 memberPoint 전달
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { rankKey, blockKey }
, new RedisValue[] { memberId, memberPoint });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : rankkey:{rankKey}, memberId:{memberId}, memberPoint:{memberPoint} - LuaScriptName:{lua_script_name}");
return (false, string.Empty, 0, string.Empty, 0, is_top_change, 0);
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
// 반환된 Lua 스크립트 결과: 최고가 입찰자 정보, 차순위 입찰자 정보, 순위자 개수
is_blocked = (false == redis_result[0].IsNull) ? (bool)redis_result[0] : true;
curr_ranker_id = (false == redis_result[1].IsNull) ? redis_result[1].ToString() : string.Empty;
curr_ranker_point = (false == redis_result[2].IsNull) ? (double)redis_result[2] : 0;
old_ranker_id = (false == redis_result[3].IsNull) ? redis_result[3].ToString() : string.Empty;
old_ranker_point = (false == redis_result[4].IsNull) ? (double)redis_result[4] : 0;
ranker_count = (false == redis_result[5].IsNull) ? (int)redis_result[5] : 0;
if (true == is_blocked)
{
var err_msg = $"Raking is Blocked !!! : rankkey:{rankKey}, memberId:{memberId}, memberPoint:{memberPoint}, blockKey:{blockKey} - LuaScriptName:{lua_script_name}";
Log.getLogger().warn(err_msg);
return (false, curr_ranker_id, curr_ranker_point, old_ranker_id, old_ranker_point, is_top_change, ranker_count);
}
// 탑랭커 변경 여부
if (false == old_ranker_id.isNullOrWhiteSpace()
&& curr_ranker_id != old_ranker_id)
{
is_top_change = true;
}
}
catch (Exception e)
{
var err_msg = $"Exception !!!, Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, rankKey:{rankKey}, memberId:{memberId}, memberPoint:{memberPoint}";
Log.getLogger().error(err_msg);
return (false, string.Empty, 0, string.Empty, 0, is_top_change, 0);
}
return (true, curr_ranker_id, curr_ranker_point, old_ranker_id, old_ranker_point, is_top_change, ranker_count);
}
//=============================================================================================
// 랭킹 맴버를 추가하고 최고 순위자와 직전 최고 순위자가 있다면 반환 하며, 차순위자는 제거 한다. - kangms
//=============================================================================================
public static async Task<(bool, string, double, string, double, bool)> tryPlaceTopRankOnly( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string rankKey, string memberId, double memberPoint
, string blockKey )
{
// Lua 스크립트 정의
var lua_script_name = "placeTopRankOnly";
var lua_script = @"
local blockKey = KEYS[2]
local rankKey = KEYS[1]
local rankerId = ARGV[1]
local rankerPoint = tonumber(ARGV[2])
local blockKeyExists = redis.call('EXISTS', blockKey)
if blockKeyExists == 1 then
return { 1, '', 0, '', 0, 0 }
end
local currentHighestRanker = redis.call('ZREVRANGE', rankKey, 0, 0, 'WITHSCORES')
local result = {}
if currentHighestRanker[1] ~= nil then
local currentHighestPoint = tonumber(currentHighestRanker[2])
local currentHighestRankerId = currentHighestRanker[1]
if rankerPoint > currentHighestPoint then
redis.call('ZREM', rankKey, currentHighestRankerId)
redis.call('ZADD', rankKey, rankerPoint, rankerId)
result = { 0, rankerId, rankerPoint, currentHighestRankerId, currentHighestPoint, 1 }
else
result = { 0, currentHighestRankerId, currentHighestPoint, '', 0, 0 }
end
else
redis.call('ZADD', rankKey, rankerPoint, rankerId)
result = { 0, rankerId, rankerPoint, '', 0, 1 }
end
return result
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return (false, string.Empty, 0, string.Empty, 0, false);
}
bool is_blocked = false;
string curr_ranker_id = string.Empty;
double curr_ranker_point = 0;
string old_ranker_id = string.Empty;
double old_ranker_point = 0;
bool is_top_change = false; // 첫번째로 등록되는 랭커일 경우에도 true 로 반환 !!!
try
{
// 루아 스크립트 실행, KEYS[1]에 rankKey, ARGV[1]에 memberId, ARGV[2]에 memberPoint 전달
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { rankKey, blockKey }
, new RedisValue[] { memberId, memberPoint });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : rankkey:{rankKey}, memberId:{memberId}, memberPoint:{memberPoint}, blockKey:{blockKey} - LuaScriptName:{lua_script_name}");
return (false, string.Empty, 0, string.Empty, 0, false);
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
// 반환된 Lua 스크립트 결과: 최고가 입찰자 정보, 차순위 입찰자 정보
is_blocked = (false == redis_result[0].IsNull) ? (bool)redis_result[0] : true;
curr_ranker_id = (false == redis_result[1].IsNull) ? redis_result[1].ToString() : string.Empty;
curr_ranker_point = (false == redis_result[2].IsNull) ? (double)redis_result[2] : 0;
old_ranker_id = (false == redis_result[3].IsNull) ? redis_result[3].ToString() : string.Empty;
old_ranker_point = (false == redis_result[4].IsNull) ? (double)redis_result[4] : 0;
is_top_change = (false == redis_result[5].IsNull) ? (bool)redis_result[5] : false;
if (true == is_blocked)
{
var err_msg = $"Raking is Blocked !!! : rankkey:{rankKey}, memberId:{memberId}, memberPoint:{memberPoint}, blockKey:{blockKey} - LuaScriptName:{lua_script_name}";
Log.getLogger().warn(err_msg);
return (false, curr_ranker_id, curr_ranker_point, old_ranker_id, old_ranker_point, is_top_change);
}
}
catch (Exception e)
{
var err_msg = $"Exception !!!, Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, rankKey:{rankKey}, memberId:{memberId}, memberPoint:{memberPoint} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return (false, string.Empty, 0, string.Empty, 0, false);
}
return (true, curr_ranker_id, curr_ranker_point, old_ranker_id, old_ranker_point, is_top_change);
}
//=============================================================================================
// 탑랭커 정보를 반환하고, 랭킹 정보를 블록 상태로 설정한다. - kangms
//=============================================================================================
public static async Task<(bool, bool, string, double)> tryGetTopRankAndSetBlock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string rankKey, string blockKey )
{
// Lua 스크립트 정의
var lua_script_name = "getTopRankAndSetBlock";
var lua_script = @"
local blockKey = KEYS[2]
local rankKey = KEYS[1]
-- 블록 상태 설정 (무제한 TTL)
redis.call('SET', blockKey, 1)
-- 현재 최고 랭커 조회
local currentHighestRanker = redis.call('ZREVRANGE', rankKey, 0, 0, 'WITHSCORES')
if currentHighestRanker[1] == nil then
return { 0, '', 0 } -- 최고 랭커가 없을 경우
end
local currentHighestRankerId = currentHighestRanker[1]
local currentHighestPoint = tonumber(currentHighestRanker[2])
-- 결과 반환 (성공, 최고 랭커 ID, 점수)
return { 1, currentHighestRankerId, currentHighestPoint }
";
// Lua 스크립트를 Redis 서버에 로드
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return (false, false, string.Empty, 0);
}
bool is_top_found = false;
string top_ranker_id = string.Empty;
double top_ranker_point = 0;
try
{
// Lua 스크립트 실행, KEYS[1]에 rankKey, KEYS[2]에 blockKey 전달
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { rankKey, blockKey }
, new RedisValue[] { } );
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : rankKey:{rankKey}, blockKey:{blockKey} - LuaScriptName:{lua_script_name}");
return (false, false, string.Empty, 0);
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
// Lua 스크립트 결과 처리: 최고 랭커 정보
is_top_found = (bool)redis_result[0];
top_ranker_id = redis_result[1]?.ToString() ?? string.Empty;
top_ranker_point = redis_result[2].IsNull ? 0 : (double)redis_result[2];
}
catch (Exception e)
{
var err_msg = $"Exception !!!, Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, rankKey:{rankKey}, blockKey:{blockKey} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return (false, false, string.Empty, 0);
}
return (is_success, is_top_found, top_ranker_id, top_ranker_point);
}
//=============================================================================================
// 랭킹 키와 블록 키를 제거 한다. - kangms
//=============================================================================================
public static async Task<bool> tryRemoveTopRankAndBlock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string rankKey, string blockKey )
{
// Lua 스크립트 정의
var lua_script_name = "removeTopRankAndBlock";
var lua_script = @"
local rankKey = KEYS[1]
local blockKey = KEYS[2]
redis.call('DEL', rankKey)
redis.call('DEL', blockKey)
return 1
";
// Lua 스크립트를 Redis 서버에 로드
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
bool is_result = false;
try
{
// Lua 스크립트 실행, KEYS[1]에 rankKey, KEYS[2]에 blockKey 전달
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { rankKey, blockKey }
, new RedisValue[] { });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : rankKey:{rankKey}, blockKey:{blockKey} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
is_result = (bool)redis_result;
}
catch (Exception e)
{
var err_msg = $"Exception !!!, Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, rankKey:{rankKey}, blockKey:{blockKey} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return false;
}
return is_success;
}
//=============================================================================================
// 데이터를 저장한 후, 즉시 Lock을 해제합니다. kangms
//=============================================================================================
public static async Task<bool> tryWriteAndReleaseLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string lockKey, string lockValue
, string toWriteDataKey, string toWriteDataValue )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "writeAndReleaseLock";
var lua_script = @"
local currentLock = redis.call('GET', KEYS[1]) -- Get current lock value (write lock key)
local ownershipKey = ARGV[1] -- Ownership key (e.g., user or session ID)
local newData = ARGV[2] -- New data to store
if currentLock == false then
-- Lock doesn't exist, set lock, store data
redis.call('SET', KEYS[1], ownershipKey)
redis.call('SET', KEYS[2], newData) -- Store new data
redis.call('DEL', KEYS[1]) -- Immediately release lock
return 1
elseif currentLock == ownershipKey then
-- Lock exists and ownership matches, update data
redis.call('SET', KEYS[2], newData)
redis.call('DEL', KEYS[1]) -- Immediately release lock
return 1
else
-- Lock exists but ownership doesn't match
return 0
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { lockKey, toWriteDataKey }
, new RedisValue[] { lockValue, toWriteDataValue });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : lockKey:{lockKey}, toWriteDataKey:{toWriteDataKey} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!! - LuaScriptName:{lua_script_name}");
var return_value = (int)redis_result;
if (0 == return_value)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_value} - LuaScriptName:{lua_script_name}");
return false;
}
}
catch (Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, lockKey:{lockKey}, toWriteDataKey:{toWriteDataKey} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return false;
}
return true;
}
//=============================================================================================
// 데이터를 저장하면서 WriteLock을 설정하고 TTL을 유지한다. WriteLock을 해제하지 않고 유지한다. - kangms
//=============================================================================================
public static async Task<bool> tryWriteWithLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string lockKey, string lockValue
, string toWriteDataKey, string toWriteDataValue
, Int32 ttlSec )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "writeWithLock";
var lua_script = @"
local currentLock = redis.call('GET', KEYS[1]) -- Get current lock value (write lock key)
local ownershipKey = ARGV[1] -- Ownership key (e.g., user or session ID)
local newData = ARGV[2] -- New data to store
local ttl = tonumber(ARGV[3]) -- TTL 값 (초 단위)
if currentLock == false then
-- Lock doesn't exist, set lock, store data, and set TTL
redis.call('SET', KEYS[1], ownershipKey)
redis.call('EXPIRE', KEYS[1], ttl) -- Set TTL for the lock
redis.call('SET', KEYS[2], newData) -- Store new data
return 1 -- 성공시 1 반환
elseif currentLock == ownershipKey then
-- Lock exists and ownership matches, update data
redis.call('SET', KEYS[2], newData)
return 1 -- 성공시 1 반환
else
-- Lock exists but ownership doesn't match
return 0 -- 실패시 0 반환
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { lockKey, toWriteDataKey }
, new RedisValue[] { lockValue, toWriteDataValue, ttlSec });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : lockKey:{lockKey}, toWriteDataKey:{toWriteDataKey} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!! - LuaScriptName:{lua_script_name}");
var return_value = (int)redis_result;
if (0 == return_value)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_value} - LuaScriptName:{lua_script_name}");
return false;
}
}
catch (Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, lockKey:{lockKey}, toWriteDataKey:{toWriteDataKey}, ttlSec:{ttlSec} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return false;
}
return true;
}
//=============================================================================================
// WriteLock 소유 여부와 상관없이 toReadDataKey로 데이터를 읽기만 한다. - kangms
//=============================================================================================
public static async Task<(bool, string)> tryReadWithLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string lockKey, string lockValue
, string toReadDataKey
, Int32 ttlSec )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "readWithLock";
var lua_script = @"
local currentLock = redis.call('GET', KEYS[1]) -- 현재 잠금 상태 확인
local ownershipKey = ARGV[1] -- 소유권 키 (예: 사용자 ID 또는 세션 ID)
local ttl = tonumber(ARGV[2]) -- TTL 값 (초 단위)
if currentLock == ownershipKey then
-- 소유권이 일치하면 TTL 갱신 및 데이터 반환
redis.call('EXPIRE', KEYS[1], ttl) -- TTL 갱신
local data = redis.call('GET', KEYS[2]) -- 데이터 가져오기 (nil 일수 있다)
return { 1, data } -- 성공: {1, 데이터} 형태로 반환
else
-- 소유권이 일치하지 않으면 실패 메시지 반환
return { 0, nil }
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return (false, string.Empty);
}
var return_data = string.Empty;
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { lockKey, toReadDataKey }
, new RedisValue[] { lockValue, ttlSec } );
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : lockKey:{lockKey}, toReadDataKey:{toReadDataKey} - LuaScriptName:{lua_script_name}");
return (false, string.Empty);
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!! - LuaScriptName:{lua_script_name}");
var return_result = (false == redis_result[0].IsNull) ? (int)redis_result[0] : 0;
return_data = (false == redis_result[1].IsNull) ? redis_result[1].ToString() : string.Empty;
if (0 == return_result)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_result} - LuaScriptName:{lua_script_name}");
return (false, string.Empty);
}
}
catch(Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, lockKey:{lockKey}, toReadDataKey:{toReadDataKey}, ttlSec:{ttlSec} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return (false, string.Empty);
}
return (true, return_data);
}
//=============================================================================================
// WriteLock 소유 여부와 상관없이 toReadDataKey로 데이터를 읽기만 한다. - kangms
//=============================================================================================
public static async Task<(bool, string)> tryReadWithNoLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string toReadDataKey )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "readWithNoLock";
var lua_script = @"
local data = redis.call('GET', KEYS[1]) -- key에 해당하는 data 반환
if data then
return { 1, data } -- 성공시 1과 데이터 반환
else
return { 0, nil } -- 실패시 0과 nil 반환
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return (false, string.Empty);
}
var return_data = string.Empty;
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { toReadDataKey } );
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : toReadDataKey:{toReadDataKey} - LuaScriptName:{lua_script_name}");
return (false, string.Empty);
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!! - LuaScriptName:{lua_script_name}");
var return_result = (false == redis_result[0].IsNull) ? (int)redis_result[0] : 0;
return_data = (false == redis_result[1].IsNull) ? redis_result[1].ToString() : string.Empty;
if (0 == return_result)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_result} - LuaScriptName:{lua_script_name}");
return (false, string.Empty);
}
}
catch(Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, toReadDataKey:{toReadDataKey} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return (false, string.Empty);
}
return (true, return_data);
}
//=============================================================================================
// 현재 키를 비교하고 같다면 새로운 값으로 변경 한다. - kangms
//=============================================================================================
public static async Task<bool> compareAndSetWithLua( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string key
, string expectedValue, string newValue )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "compareAndSet";
var lua_script = @"
local current = redis.call('GET', KEYS[1])
if current == nil or current == false or current == ARGV[1] then
redis.call('SET', KEYS[1], ARGV[2])
return 1 -- 성공시 1 반환
else
return 0 -- 실패시 0 반환
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { key }
, new RedisValue[] { expectedValue, newValue });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : key:{key}, expectedValue:{expectedValue}, newValue:{newValue} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!! - LuaScriptName:{lua_script_name}");
var return_value = (int)redis_result;
if (0 == return_value)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_value} - LuaScriptName:{lua_script_name}");
return false;
}
}
catch(Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, key:{key}, expectedValue:{expectedValue}, newValue:{newValue} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return false;
}
return true;
}
//=============================================================================================
// 현재 lockKey가 존재하는지 체크하고 lockKey가 없거나, lockKey의 lockValue값이 같을 경우 true 반환, 그 외에는 false-를 반환 한다. - kangms
//=============================================================================================
public static async Task<bool> tryAcquireLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string lockKey, string lockValue
, Int32 ttlSec )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "acquireLock";
var lua_script = @"
local ttl = tonumber(ARGV[2]) -- TTL 값 (초 단위)
local current = redis.call('GET', KEYS[1])
if current == nil or current == false or current == ARGV[1] then
redis.call('SET', KEYS[1], ARGV[1], 'EX', ttl)
return 1 -- 성공시 1 반환
else
return 0 -- 실패시 0 반환
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { lockKey }
, new RedisValue[] { lockValue, ttlSec });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : lockKey:{lockKey}, lockValue:{lockValue}, ttlSec:{ttlSec} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
var return_value = (int)redis_result;
if (0 == return_value)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_value} - LuaScriptName:{lua_script_name}");
return false;
}
}
catch(Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, lockKey:{lockKey}, lockValue:{lockValue}, ttlSec:{ttlSec} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
return false;
}
return true;
}
//=============================================================================================
// 현재 lockKey가 존재하는지 체크하고 lockKey가 있고 lockValue가 같다면 true 를 반환하고, 그 외에는 false를 반환 한다. - kangms
//=============================================================================================
public static async Task<bool> tyrReleaseLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string lockKey, string lockValue )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "releaseLock";
var lua_script = @"
local current = redis.call('GET', KEYS[1])
if current == ARGV[1] then
redis.call('DEL', KEYS[1])
return 1 -- 성공시 1 반환
else
return 0 -- 실패시 0 반환
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { lockKey }
, new RedisValue[] { lockValue });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : lockKey:{lockKey}, lockValue:{lockValue} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
var return_value = (int)redis_result;
if (0 == return_value)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_value} - LuaScriptName:{lua_script_name}");
return false;
}
}
catch (Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, lockKey:{lockKey}, lockValue:{lockValue} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
}
return true;
}
//=============================================================================================
// 현재 lockKey가 존재하는지 체크하고 lockKey의 lockValue값이 같을 경우 ttlMSec 설정 한다. 성공시 1, 실패시 0을 반환 한다. - kangms
//=============================================================================================
public static async Task<bool> tyrResetTTLWithLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string lockKey, string lockValue
, Int32 ttlMSec )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "resetTtlWithLock";
var lua_script = @"
local currentLock = redis.call('GET', KEYS[1]) -- Get current lock value (write lock key)
local ownershipKey = ARGV[1] -- Ownership key (e.g., user or session ID)
local ttl = tonumber(ARGV[2]) -- New TTL in seconds
if currentLock == ownershipKey then
-- 소유권이 일치하면 TTL 갱신
redis.call('EXPIRE', KEYS[1], ttl)
return 1 -- Success
else
-- 소유권이 일치하지 않음
return 0 -- Failure
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { lockKey }
, new RedisValue[] { lockValue, ttlMSec });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : lockKey:{lockKey}, ttl:{ttlMSec} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
var return_value = (int)redis_result;
if (0 == return_value)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_value} - LuaScriptName:{lua_script_name}");
return false;
}
}
catch (Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, lockKey:{lockKey}, ttl:{ttlMSec} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
}
return true;
}
//=============================================================================================
// targetKey로 TTL을 재설정할 키를 전달받고, ttl로 새로운 TTL 값을 전달받습니다. 성공시 true, 실패시 false을 반환 한다. - kangms
//=============================================================================================
public static async Task<bool> tyrResetTTLWithNoLock( this RedisWithLuaScriptExecutorBase luaScriptExecutor
, string targetKey, Int32 ttlMSec )
{
// lua 스크립트 레디스 set 구문 실행 구성
var lua_script_name = "resetTtlWithNoLock";
var lua_script = @"
local ttl = tonumber(ARGV[1]) -- TTL in seconds
local exists = redis.call('EXISTS', KEYS[1])
if exists == 1 then
-- 키가 존재할 경우 TTL을 재설정
redis.call('EXPIRE', KEYS[1], ttl)
return 1 -- 성공 시 1 반환
else
-- 키가 존재하지 않으면 실패
return 0 -- 실패 시 0 반환
end
";
var is_success = await luaScriptExecutor.tryLoadLuaScriptToRedisServer(lua_script_name, lua_script);
if (false == is_success)
{
return false;
}
try
{
(is_success, var redis_result) = await luaScriptExecutor.tryExecuteLuaScriptAsync( lua_script_name
, new RedisKey[] { targetKey }
, new RedisValue[] { ttlMSec });
if (false == is_success)
{
Log.getLogger().error($"Failed to RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : targetKey:{targetKey}, ttl:{ttlMSec} - LuaScriptName:{lua_script_name}");
return false;
}
NullReferenceCheckHelper.throwIfNull(redis_result, () => $"redis_result is null !!!");
var return_value = (int)redis_result;
if (0 == return_value)
{
Log.getLogger().error($"Failed to execute LuaScript in Redis !!! : retrunValue:{return_value} - LuaScriptName:{lua_script_name}");
return false;
}
}
catch (Exception e)
{
var err_msg = $"Exception !!!, RedisWithLuaScriptExecutorBase.tryExecuteLuaScriptAsync() !!! : exception:{e}, targetKey:{targetKey}, ttl:{ttlMSec} - LuaScriptName:{lua_script_name}";
Log.getLogger().error(err_msg);
}
return true;
}
}