Commit c5034d50 authored by Nick Craver's avatar Nick Craver

Cleanup: LuaScript

parent 78dc930d
...@@ -20,26 +20,26 @@ public sealed class LuaScript ...@@ -20,26 +20,26 @@ public sealed class LuaScript
{ {
// Since the mapping of "script text" -> LuaScript doesn't depend on any particular details of // Since the mapping of "script text" -> LuaScript doesn't depend on any particular details of
// the redis connection itself, this cache is global. // the redis connection itself, this cache is global.
static readonly ConcurrentDictionary<string, WeakReference> Cache = new ConcurrentDictionary<string, WeakReference>(); private static readonly ConcurrentDictionary<string, WeakReference> Cache = new ConcurrentDictionary<string, WeakReference>();
/// <summary> /// <summary>
/// The original Lua script that was used to create this. /// The original Lua script that was used to create this.
/// </summary> /// </summary>
public string OriginalScript { get; private set; } public string OriginalScript { get; }
/// <summary> /// <summary>
/// The Lua script that will actually be sent to Redis for execution. /// The Lua script that will actually be sent to Redis for execution.
/// ///
/// All @-prefixed parameter names have been replaced at this point. /// All @-prefixed parameter names have been replaced at this point.
/// </summary> /// </summary>
public string ExecutableScript { get; private set; } public string ExecutableScript { get; }
// Arguments are in the order they have to passed to the script in // Arguments are in the order they have to passed to the script in
internal string[] Arguments { get; private set; } internal string[] Arguments { get; }
bool HasArguments => Arguments != null && Arguments.Length > 0; private bool HasArguments => Arguments?.Length > 0;
Hashtable ParameterMappers; private readonly Hashtable ParameterMappers;
internal LuaScript(string originalScript, string executableScript, string[] arguments) internal LuaScript(string originalScript, string executableScript, string[] arguments)
{ {
...@@ -61,8 +61,7 @@ internal LuaScript(string originalScript, string executableScript, string[] argu ...@@ -61,8 +61,7 @@ internal LuaScript(string originalScript, string executableScript, string[] argu
{ {
try try
{ {
WeakReference ignored; Cache.TryRemove(OriginalScript, out _);
Cache.TryRemove(OriginalScript, out ignored);
} }
catch { } catch { }
} }
...@@ -72,28 +71,22 @@ internal LuaScript(string originalScript, string executableScript, string[] argu ...@@ -72,28 +71,22 @@ internal LuaScript(string originalScript, string executableScript, string[] argu
/// Existing LuaScripts will continue to work, but future calls to LuaScript.Prepare /// Existing LuaScripts will continue to work, but future calls to LuaScript.Prepare
/// return a new LuaScript instance. /// return a new LuaScript instance.
/// </summary> /// </summary>
public static void PurgeCache() public static void PurgeCache() => Cache.Clear();
{
Cache.Clear();
}
/// <summary> /// <summary>
/// Returns the number of cached LuaScripts. /// Returns the number of cached LuaScripts.
/// </summary> /// </summary>
public static int GetCachedScriptCount() public static int GetCachedScriptCount() => Cache.Count;
{
return Cache.Count;
}
/// <summary> /// <summary>
/// Prepares a Lua script with named parameters to be run against any Redis instance. /// Prepares a Lua script with named parameters to be run against any Redis instance.
/// </summary> /// </summary>
/// <param name="script">The script to prepare.</param>
public static LuaScript Prepare(string script) public static LuaScript Prepare(string script)
{ {
LuaScript ret; LuaScript ret;
WeakReference weakRef; if (!Cache.TryGetValue(script, out WeakReference weakRef) || (ret = (LuaScript)weakRef.Target) == null)
if (!Cache.TryGetValue(script, out weakRef) || (ret = (LuaScript)weakRef.Target) == null)
{ {
ret = ScriptParameterMapper.PrepareScript(script); ret = ScriptParameterMapper.PrepareScript(script);
Cache[script] = new WeakReference(ret); Cache[script] = new WeakReference(ret);
...@@ -117,9 +110,7 @@ internal void ExtractParameters(object ps, RedisKey? keyPrefix, out RedisKey[] k ...@@ -117,9 +110,7 @@ internal void ExtractParameters(object ps, RedisKey? keyPrefix, out RedisKey[] k
mapper = (Func<object, RedisKey?, ScriptParameterMapper.ScriptParameters>)ParameterMappers[psType]; mapper = (Func<object, RedisKey?, ScriptParameterMapper.ScriptParameters>)ParameterMappers[psType];
if (mapper == null) if (mapper == null)
{ {
string missingMember; if (!ScriptParameterMapper.IsValidParameterHash(psType, this, out string missingMember, out string badMemberType))
string badMemberType;
if (!ScriptParameterMapper.IsValidParameterHash(psType, this, out missingMember, out badMemberType))
{ {
if (missingMember != null) if (missingMember != null)
{ {
...@@ -148,24 +139,26 @@ internal void ExtractParameters(object ps, RedisKey? keyPrefix, out RedisKey[] k ...@@ -148,24 +139,26 @@ internal void ExtractParameters(object ps, RedisKey? keyPrefix, out RedisKey[] k
/// <summary> /// <summary>
/// Evaluates this LuaScript against the given database, extracting parameters from the passed in object if any. /// Evaluates this LuaScript against the given database, extracting parameters from the passed in object if any.
/// </summary> /// </summary>
/// <param name="db">The redis databse to evaluate against.</param>
/// <param name="ps">The parameter object to use.</param>
/// <param name="withKeyPrefix">The key prefix to use, if any.</param>
/// <param name="flags">The command flags to use.</param>
public RedisResult Evaluate(IDatabase db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None) public RedisResult Evaluate(IDatabase db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None)
{ {
RedisKey[] keys; ExtractParameters(ps, withKeyPrefix, out RedisKey[] keys, out RedisValue[] args);
RedisValue[] args;
ExtractParameters(ps, withKeyPrefix, out keys, out args);
return db.ScriptEvaluate(ExecutableScript, keys, args, flags); return db.ScriptEvaluate(ExecutableScript, keys, args, flags);
} }
/// <summary> /// <summary>
/// Evaluates this LuaScript against the given database, extracting parameters from the passed in object if any. /// Evaluates this LuaScript against the given database, extracting parameters from the passed in object if any.
/// </summary> /// </summary>
/// <param name="db">The redis databse to evaluate against.</param>
/// <param name="ps">The parameter object to use.</param>
/// <param name="withKeyPrefix">The key prefix to use, if any.</param>
/// <param name="flags">The command flags to use.</param>
public Task<RedisResult> EvaluateAsync(IDatabaseAsync db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None) public Task<RedisResult> EvaluateAsync(IDatabaseAsync db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None)
{ {
RedisKey[] keys; ExtractParameters(ps, withKeyPrefix, out RedisKey[] keys, out RedisValue[] args);
RedisValue[] args;
ExtractParameters(ps, withKeyPrefix, out keys, out args);
return db.ScriptEvaluateAsync(ExecutableScript, keys, args, flags); return db.ScriptEvaluateAsync(ExecutableScript, keys, args, flags);
} }
...@@ -175,15 +168,16 @@ public Task<RedisResult> EvaluateAsync(IDatabaseAsync db, object ps = null, Redi ...@@ -175,15 +168,16 @@ public Task<RedisResult> EvaluateAsync(IDatabaseAsync db, object ps = null, Redi
/// ///
/// Note: the FireAndForget command flag cannot be set /// Note: the FireAndForget command flag cannot be set
/// </summary> /// </summary>
/// <param name="server">The server to load the script on.</param>
/// <param name="flags">The command flags to use.</param>
public LoadedLuaScript Load(IServer server, CommandFlags flags = CommandFlags.None) public LoadedLuaScript Load(IServer server, CommandFlags flags = CommandFlags.None)
{ {
if (flags.HasFlag(CommandFlags.FireAndForget)) if ((flags & CommandFlags.FireAndForget) != 0)
{ {
throw new ArgumentOutOfRangeException(nameof(flags), "Loading a script cannot be FireAndForget"); throw new ArgumentOutOfRangeException(nameof(flags), "Loading a script cannot be FireAndForget");
} }
var hash = server.ScriptLoad(ExecutableScript, flags); var hash = server.ScriptLoad(ExecutableScript, flags);
return new LoadedLuaScript(this, hash); return new LoadedLuaScript(this, hash);
} }
...@@ -193,15 +187,16 @@ public LoadedLuaScript Load(IServer server, CommandFlags flags = CommandFlags.No ...@@ -193,15 +187,16 @@ public LoadedLuaScript Load(IServer server, CommandFlags flags = CommandFlags.No
/// ///
/// Note: the FireAndForget command flag cannot be set /// Note: the FireAndForget command flag cannot be set
/// </summary> /// </summary>
/// <param name="server">The server to load the script on.</param>
/// <param name="flags">The command flags to use.</param>
public async Task<LoadedLuaScript> LoadAsync(IServer server, CommandFlags flags = CommandFlags.None) public async Task<LoadedLuaScript> LoadAsync(IServer server, CommandFlags flags = CommandFlags.None)
{ {
if (flags.HasFlag(CommandFlags.FireAndForget)) if ((flags & CommandFlags.FireAndForget) != 0)
{ {
throw new ArgumentOutOfRangeException(nameof(flags), "Loading a script cannot be FireAndForget"); throw new ArgumentOutOfRangeException(nameof(flags), "Loading a script cannot be FireAndForget");
} }
var hash = await server.ScriptLoadAsync(ExecutableScript, flags).ForAwait(); var hash = await server.ScriptLoadAsync(ExecutableScript, flags).ForAwait();
return new LoadedLuaScript(this, hash); return new LoadedLuaScript(this, hash);
} }
} }
...@@ -240,7 +235,7 @@ public sealed class LoadedLuaScript ...@@ -240,7 +235,7 @@ public sealed class LoadedLuaScript
/// ///
/// This is sent to Redis instead of ExecutableScript during Evaluate and EvaluateAsync calls. /// This is sent to Redis instead of ExecutableScript during Evaluate and EvaluateAsync calls.
/// </summary> /// </summary>
public byte[] Hash { get; private set; } public byte[] Hash { get; }
// internal for testing purposes only // internal for testing purposes only
internal LuaScript Original; internal LuaScript Original;
...@@ -257,12 +252,13 @@ internal LoadedLuaScript(LuaScript original, byte[] hash) ...@@ -257,12 +252,13 @@ internal LoadedLuaScript(LuaScript original, byte[] hash)
/// This method sends the SHA1 hash of the ExecutableScript instead of the script itself. If the script has not /// This method sends the SHA1 hash of the ExecutableScript instead of the script itself. If the script has not
/// been loaded into the passed Redis instance it will fail. /// been loaded into the passed Redis instance it will fail.
/// </summary> /// </summary>
/// <param name="db">The redis databse to evaluate against.</param>
/// <param name="ps">The parameter object to use.</param>
/// <param name="withKeyPrefix">The key prefix to use, if any.</param>
/// <param name="flags">The command flags to use.</param>
public RedisResult Evaluate(IDatabase db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None) public RedisResult Evaluate(IDatabase db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None)
{ {
RedisKey[] keys; Original.ExtractParameters(ps, withKeyPrefix, out RedisKey[] keys, out RedisValue[] args);
RedisValue[] args;
Original.ExtractParameters(ps, withKeyPrefix, out keys, out args);
return db.ScriptEvaluate(Hash, keys, args, flags); return db.ScriptEvaluate(Hash, keys, args, flags);
} }
...@@ -272,12 +268,13 @@ public RedisResult Evaluate(IDatabase db, object ps = null, RedisKey? withKeyPre ...@@ -272,12 +268,13 @@ public RedisResult Evaluate(IDatabase db, object ps = null, RedisKey? withKeyPre
/// This method sends the SHA1 hash of the ExecutableScript instead of the script itself. If the script has not /// This method sends the SHA1 hash of the ExecutableScript instead of the script itself. If the script has not
/// been loaded into the passed Redis instance it will fail. /// been loaded into the passed Redis instance it will fail.
/// </summary> /// </summary>
/// <param name="db">The redis databse to evaluate against.</param>
/// <param name="ps">The parameter object to use.</param>
/// <param name="withKeyPrefix">The key prefix to use, if any.</param>
/// <param name="flags">The command flags to use.</param>
public Task<RedisResult> EvaluateAsync(IDatabaseAsync db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None) public Task<RedisResult> EvaluateAsync(IDatabaseAsync db, object ps = null, RedisKey? withKeyPrefix = null, CommandFlags flags = CommandFlags.None)
{ {
RedisKey[] keys; Original.ExtractParameters(ps, withKeyPrefix, out RedisKey[] keys, out RedisValue[] args);
RedisValue[] args;
Original.ExtractParameters(ps, withKeyPrefix, out keys, out args);
return db.ScriptEvaluateAsync(Hash, keys, args, flags); return db.ScriptEvaluateAsync(Hash, keys, args, flags);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment