초기커밋
This commit is contained in:
258
ServerCore/Redis/RedisConnectorBase.cs
Normal file
258
ServerCore/Redis/RedisConnectorBase.cs
Normal file
@@ -0,0 +1,258 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
||||
using Renci.SshNet;
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.Extensions.Core;
|
||||
using StackExchange.Redis.Extensions.Core.Abstractions;
|
||||
using StackExchange.Redis.Extensions.Core.Configuration;
|
||||
using StackExchange.Redis.Extensions.Core.Implementations;
|
||||
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
public abstract class RedisConnectorBase
|
||||
{
|
||||
private readonly string m_connection_string = string.Empty;
|
||||
private readonly RedisConfiguration m_redis_config;
|
||||
|
||||
private ConnectionMultiplexer? m_connector;
|
||||
|
||||
private SshClient? m_ssh_tuneling;
|
||||
private ForwardedPortLocal? m_forwarded_port_local;
|
||||
|
||||
public RedisConnectorBase(string connectionString)
|
||||
{
|
||||
m_redis_config = new RedisConfiguration()
|
||||
{
|
||||
ConnectionString = connectionString,
|
||||
};
|
||||
|
||||
m_connection_string = connectionString;
|
||||
}
|
||||
|
||||
public async Task<bool> tryConnectAsync()
|
||||
{
|
||||
if (false == isConfigureConnectionInfo())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (false == isConnected())
|
||||
{
|
||||
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
|
||||
var config_options = ConfigurationOptions.Parse(m_connection_string);
|
||||
|
||||
var connection = await ConnectionMultiplexer.ConnectAsync(config_options);
|
||||
if (null == connection)
|
||||
{
|
||||
var err_msg = $"Failed to connectAsync RedisServer !!! : ConnectString:{m_connection_string}";
|
||||
throw new Exception(err_msg);
|
||||
}
|
||||
|
||||
m_connector = connection;
|
||||
|
||||
connection.ConnectionFailed += onConnectfailed;
|
||||
connection.ErrorMessage += onError;
|
||||
connection.InternalError += onInternalError;
|
||||
connection.ConnectionRestored += onConnectionRestored;
|
||||
connection.ConfigurationChanged += onConfigurationChanged;
|
||||
connection.ConfigurationChangedBroadcast += onMasterSlaveChanged;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
var err_msg = $"Exception !!!, Failed to perform in tryConnectAsync() !!! : exception:{e}, connectionString:{m_connection_string} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void setConnectionMultiplexer(ConnectionMultiplexer connector) => m_connector = connector;
|
||||
|
||||
public bool tryConnect()
|
||||
{
|
||||
if (false == isConfigureConnectionInfo())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (false == isConnected())
|
||||
{
|
||||
var config_options = ConfigurationOptions.Parse(m_connection_string);
|
||||
|
||||
var connection = ConnectionMultiplexer.Connect(config_options);
|
||||
if (null == connection)
|
||||
{
|
||||
var err_msg = $"Failed to connect RedisServer !!! : ConnectString:{m_connection_string}";
|
||||
throw new Exception(err_msg);
|
||||
}
|
||||
|
||||
m_connector = connection;
|
||||
|
||||
connection.ConnectionFailed += onConnectfailed;
|
||||
connection.ErrorMessage += onError;
|
||||
connection.InternalError += onInternalError;
|
||||
connection.ConnectionRestored += onConnectionRestored;
|
||||
connection.ConfigurationChanged += onConfigurationChanged;
|
||||
connection.ConfigurationChangedBroadcast += onMasterSlaveChanged;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var err_msg = $"Exception !!!, try connectAsync failed !!! : Exception:{e} - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//=========================================================================================
|
||||
// sshHost : ec2-54-69-223-245.us-west-2.compute.amazonaws.com // SSH 서버 (Jump Host or Bastion)
|
||||
// sshPort : 2211 // SSH 포트
|
||||
// sshUsername : "ubuntu" // SSH 사용자 이름
|
||||
// privateKeyPath : @"D:\SVN\Caliverse\Metaverse\trunk\Caliverse\Server\Security\SSH-Keys\pem\USWest2-KeyPair.pem" // Private Key 보안 파일 참조 경로
|
||||
// localPort : 16379 // 로컬에서 사용할 포트
|
||||
// redisHost : "clustercfg.metaverse-qa-cluster.ocif0u.usw2.cache.amazonaws.com" // Redis 클러스터 호스트
|
||||
// redisPort : 6379 // Redis 사용 포트
|
||||
// redisPassword : "wiUaVvNwX4PhBj&8"
|
||||
//=========================================================================================
|
||||
public bool startTuneling( string sshHost, int sshPort, string sshUserName, string privateKeyPath
|
||||
, int localPort, string redisHost, int redisPort, string redisPassword )
|
||||
{
|
||||
// SSH Private Key 설정
|
||||
PrivateKeyFile privateKey = new PrivateKeyFile(privateKeyPath);
|
||||
var keyFiles = new[] { privateKey };
|
||||
|
||||
try
|
||||
{
|
||||
var ssh_client = new SshClient(sshHost, sshPort, sshUserName, keyFiles);
|
||||
|
||||
// SSH 연결 시도
|
||||
ssh_client.Connect();
|
||||
|
||||
// 포트 포워딩 설정 (로컬 포트 -> 원격 Redis 호스트)
|
||||
var portForward = new ForwardedPortLocal("127.0.0.1", (uint)localPort, redisHost, (uint)redisPort);
|
||||
ssh_client.AddForwardedPort(portForward);
|
||||
|
||||
portForward.Start();
|
||||
|
||||
m_forwarded_port_local = portForward;
|
||||
m_ssh_tuneling = ssh_client;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
var err_msg = $"Exception !!!, Failed to function in startTuneling() !!! : Exception:{e}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void stopTuneling()
|
||||
{
|
||||
if (null != m_forwarded_port_local)
|
||||
{
|
||||
m_forwarded_port_local.Stop();
|
||||
m_forwarded_port_local = null;
|
||||
}
|
||||
|
||||
if(null != m_ssh_tuneling)
|
||||
{
|
||||
// SSH 연결 종료
|
||||
m_ssh_tuneling.Disconnect();
|
||||
m_ssh_tuneling = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool isConfigureConnectionInfo()
|
||||
{
|
||||
if ( 0 >= m_connection_string.Length )
|
||||
{
|
||||
var err_msg = $"Invalid Configure of RedisConnector, Empty m_connectionString !!! - {toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool isConnected()
|
||||
{
|
||||
if( null == m_connector
|
||||
|| false == m_connector.IsConnected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public IDatabase? getDatabase()
|
||||
{
|
||||
if(false == isConnected())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_connector?.GetDatabase();
|
||||
}
|
||||
|
||||
public ConnectionMultiplexer? getConnectionMultiplexer() => m_connector;
|
||||
|
||||
#region event
|
||||
protected virtual void onConnectionRestored(object? sender, ConnectionFailedEventArgs e)
|
||||
{
|
||||
Log.getLogger().warn($"Redis connection restored !!! : {e} - {toBasicString()}");
|
||||
}
|
||||
|
||||
protected void onConnectfailed(object? sender, ConnectionFailedEventArgs e)
|
||||
{
|
||||
Log.getLogger().error($"Redis connect failed !!! : ConnectionFailed:{e.Exception} - {toBasicString()}");
|
||||
}
|
||||
|
||||
protected void onError(object? sender, RedisErrorEventArgs e)
|
||||
{
|
||||
Log.getLogger().error($"Redis connection restored !!! : RedisError:{e} - {toBasicString()}");
|
||||
}
|
||||
|
||||
protected void onInternalError(object? sender, InternalErrorEventArgs e)
|
||||
{
|
||||
Log.getLogger().error($"Redis internal error !!! : InternalError:{e} - {toBasicString()}");
|
||||
}
|
||||
|
||||
protected void onConfigurationChanged(object? sender, EndPointEventArgs e)
|
||||
{
|
||||
Log.getLogger().warn($"Redis configuration changed !!! : EndPoint:{e} - {toBasicString()}");
|
||||
}
|
||||
|
||||
protected void onMasterSlaveChanged(object? sender, EndPointEventArgs e)
|
||||
{
|
||||
Log.getLogger().warn($"Redis changed master-slave !!! : EndPoint:{e.EndPoint} - {toBasicString()}");
|
||||
}
|
||||
#endregion event
|
||||
|
||||
|
||||
public string toBasicString()
|
||||
{
|
||||
return $"RedisConnector - Class:{this.getTypeName()}, ConnectString:{m_connection_string}";
|
||||
}
|
||||
}
|
||||
39
ServerCore/Redis/RedisTransaction.cs
Normal file
39
ServerCore/Redis/RedisTransaction.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using StackExchange.Redis;
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
public class RedisTransaction : IDisposable
|
||||
{
|
||||
private readonly ITransaction m_transaction;
|
||||
private bool m_committed = false;
|
||||
|
||||
public RedisTransaction(IDatabase db)
|
||||
{
|
||||
m_transaction = db.CreateTransaction();
|
||||
}
|
||||
|
||||
public async Task commitAsync()
|
||||
{
|
||||
m_committed = await m_transaction.ExecuteAsync();
|
||||
}
|
||||
|
||||
// Dispose에서 트랜잭션 해제 처리
|
||||
public void Dispose()
|
||||
{
|
||||
if (false == m_committed)
|
||||
{
|
||||
// 트랜잭션을 취소
|
||||
m_transaction.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
public ITransaction getTransaction() => m_transaction;
|
||||
}
|
||||
98
ServerCore/Redis/RedisWithLuaScriptExecutorBase.cs
Normal file
98
ServerCore/Redis/RedisWithLuaScriptExecutorBase.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
using StackExchange.Redis;
|
||||
|
||||
|
||||
namespace ServerCore;
|
||||
|
||||
//=============================================================================================
|
||||
// Server-side transactional scripting in Redis
|
||||
//
|
||||
//=============================================================================================
|
||||
public abstract class RedisWithLuaScriptExecutorBase
|
||||
{
|
||||
private readonly RedisConnectorBase m_connector;
|
||||
|
||||
private readonly ConcurrentDictionary<string, LoadedLuaScript> m_loaded_lua_scripts = new();
|
||||
|
||||
public RedisWithLuaScriptExecutorBase(RedisConnectorBase connector)
|
||||
{
|
||||
m_connector = connector;
|
||||
}
|
||||
|
||||
public async Task<bool> tryLoadLuaScriptToRedisServer(string scriptName, string luaScript)
|
||||
{
|
||||
var err_msg = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
if (true == m_loaded_lua_scripts.ContainsKey(scriptName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var multiplexer = m_connector.getConnectionMultiplexer();
|
||||
NullReferenceCheckHelper.throwIfNull(multiplexer, () => $"multiplexer is null !!!");
|
||||
|
||||
var end_points = multiplexer.GetEndPoints();
|
||||
NullReferenceCheckHelper.throwIfNull(end_points, () => $"end_points is null !!!");
|
||||
|
||||
var curr_server = multiplexer.GetServer(end_points[0]);
|
||||
NullReferenceCheckHelper.throwIfNull(curr_server, () => $"curr_server is null !!!");
|
||||
|
||||
var prepared_lug_script = LuaScript.Prepare(luaScript);
|
||||
NullReferenceCheckHelper.throwIfNull(prepared_lug_script, () => $"prepared_lug_script is null !!!");
|
||||
|
||||
var uploaded_lua_script = await prepared_lug_script.LoadAsync(curr_server);
|
||||
NullReferenceCheckHelper.throwIfNull(uploaded_lua_script, () => $"uploaded_lua_script is null !!!");
|
||||
|
||||
m_loaded_lua_scripts[scriptName] = uploaded_lua_script;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
err_msg = $"Exception !!!, Failed to function in tryLoadScriptAsync() !!! : exception:{e}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<(bool, RedisResult?)> tryExecuteLuaScriptAsync(string scriptName, RedisKey[] keys, RedisValue[]? args = null)
|
||||
{
|
||||
RedisResult? redis_result = null;
|
||||
var err_msg = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var connected_redis_db = m_connector.getDatabase();
|
||||
NullReferenceCheckHelper.throwIfNull(connected_redis_db, () => $"connected_redis_db is null !!!");
|
||||
|
||||
if (false == m_loaded_lua_scripts.TryGetValue(scriptName, out var found_lua_script))
|
||||
{
|
||||
err_msg = $"Not found LuaScript !!! : scriptName:{scriptName}";
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
var script = found_lua_script.ExecutableScript;
|
||||
|
||||
redis_result = await connected_redis_db.ScriptEvaluateAsync(script, keys, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
err_msg = $"Exception !!!, Failed to ScriptEvaluateAsync() !!! : exception:{e} - LuaScriptName:{scriptName}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
return (true, redis_result);
|
||||
}
|
||||
}
|
||||
886
ServerCore/Redis/RedisWithLuaScriptExecutorHelper.cs
Normal file
886
ServerCore/Redis/RedisWithLuaScriptExecutorHelper.cs
Normal file
@@ -0,0 +1,886 @@
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user