Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StackExchange.Redis Batch Integration + tests #159

Merged
merged 6 commits into from
Sep 28, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,22 @@
"method": "ExecuteAsyncImpl",
"signature": "10 01 05 1C 1C 1C 1C 1C 1C"
}
},
{
"caller": {
"assembly": "StackExchange.Redis"
},
"target": {
"assembly": "StackExchange.Redis",
"type": "StackExchange.Redis.RedisBase",
"method": "ExecuteAsync"
},
"wrapper": {
"assembly": "Datadog.Trace.ClrProfiler.Managed, Version=0.3.2.0, Culture=neutral, PublicKeyToken=def86d061d0d2eeb",
"type": "Datadog.Trace.ClrProfiler.Integrations.StackExchange.Redis.RedisBatch",
"method": "ExecuteAsync",
"signature": "10 01 04 1C 1C 1C 1C 1C"
}
}
]
},
Expand Down
180 changes: 178 additions & 2 deletions samples/Samples.RedisCore/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ServiceStack.Redis;
using StackExchange.Redis;

Expand Down Expand Up @@ -66,9 +67,9 @@ private static void RunStackExchange(string prefix)
prefix += "StackExchange.Redis.";

Console.WriteLine($"Testing StackExchange.Redis {prefix}");
using (var redis = ConnectionMultiplexer.Connect("localhost"))
using (var redis = ConnectionMultiplexer.Connect("localhost,allowAdmin=true"))
{
var db = redis.GetDatabase();
var db = redis.GetDatabase(1);

db.StringSet($"{prefix}INCR", "0");

Expand All @@ -83,13 +84,188 @@ private static void RunStackExchange(string prefix)
{ "GET", () => db.StringGet($"{prefix}INCR") },
{ "TIME", () => db.Execute("TIME") },
});


var batch = db.CreateBatch();
var batchPending = RunStackExchangeAsync(prefix + "Batch.", batch);
batch.Execute();
foreach (var item in batchPending)
{
try
{
Console.WriteLine($"{item.Item1}: {TaskResult(item.Item2)}");
}
catch (Exception e)
{
while (e.InnerException != null)
{
e = e.InnerException;
}
Console.WriteLine($"{item.Item1}: {e.Message}");
}
}

try
{
redis.GetServer("localhost:6379").FlushDatabase(1);
}
catch
{
}
}
}

private static TupleList<string, Task> RunStackExchangeAsync(string prefix, IDatabaseAsync db)
{
var tasks = new TupleList<string, Func<Task>>()
{
{ "DebugObjectAsync", () => db.DebugObjectAsync($"{prefix}DebugObjectAsync") },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You weren't kidding, that's a lot of methods.

{ "ExecuteAsync", () => db.ExecuteAsync("DDCUSTOM", "COMMAND") },
{ "GeoAddAsync", () => db.GeoAddAsync($"{prefix}GeoAddAsync", new GeoEntry(1.5, 2.5, "member")) },
{ "GeoDistanceAsync", () => db.GeoDistanceAsync($"{prefix}GeoDistanceAsync", "member1", "member2") },
{ "GeoHashAsync", () => db.GeoHashAsync($"{prefix}GeoHashAsync", "member") },
{ "GeoPositionAsync", () => db.GeoPositionAsync($"{prefix}GeoPositionAsync", "member") },
{ "GeoRadiusAsync", () => db.GeoRadiusAsync($"{prefix}GeoRadiusAsync", "member", 2.3) },
{ "GeoRemoveAsync", () => db.GeoRemoveAsync($"{prefix}GeoRemoveAsync", "member") },
{ "HashDecrementAsync", () => db.HashDecrementAsync($"{prefix}HashDecrementAsync", "hashfield", 4.5) },
{ "HashDeleteAsync", () => db.HashDeleteAsync($"{prefix}HashDeleteAsync", "hashfield") },
{ "HashExistsAsync", () => db.HashExistsAsync($"{prefix}HashExistsAsync", "hashfield") },
{ "HashGetAllAsync", () => db.HashGetAllAsync($"{prefix}HashGetAllAsync") },
{ "HashIncrementAsync", () => db.HashIncrementAsync($"{prefix}HashIncrementAsync", "hashfield", 1.5) },
{ "HashKeysAsync", () => db.HashKeysAsync($"{prefix}HashKeysAsync") },
{ "HashLengthAsync", () => db.HashLengthAsync($"{prefix}HashLengthAsync") },
{ "HashSetAsync", () => db.HashSetAsync($"{prefix}HashSetAsync", new HashEntry[] { new HashEntry("x", "y") }) },
{ "HashValuesAsync", () => db.HashValuesAsync($"{prefix}HashValuesAsync") },
{ "HyperLogLogAddAsync", () => db.HyperLogLogAddAsync($"{prefix}HyperLogLogAddAsync", "value") },
{ "HyperLogLogLengthAsync", () => db.HyperLogLogLengthAsync($"{prefix}HyperLogLogLengthAsync") },
{ "HyperLogLogMergeAsync", () => db.HyperLogLogMergeAsync($"{prefix}HyperLogLogMergeAsync", new RedisKey[] { "key1", "key2" }) },
{ "IdentifyEndpointAsync", () => db.IdentifyEndpointAsync() },
{ "KeyDeleteAsync", () => db.KeyDeleteAsync("key") },
{ "KeyDumpAsync", () => db.KeyDumpAsync("key") },
{ "KeyExistsAsync", () => db.KeyExistsAsync("key") },
{ "KeyExpireAsync", () => db.KeyExpireAsync("key", DateTime.Now) },
// () => db.KeyMigrateAsync("key", ???)
{ "KeyMoveAsync", () => db.KeyMoveAsync("key", 1) },
{ "KeyPersistAsync", () => db.KeyPersistAsync("key") },
{ "KeyRandomAsync", () => db.KeyRandomAsync() },
{ "KeyRenameAsync", () => db.KeyRenameAsync("key1", "key2") },
{ "KeyRestoreAsync", () => db.KeyRestoreAsync("key", new byte[] { 0,1,2,3,4 }) },
{ "KeyTimeToLiveAsync", () => db.KeyTimeToLiveAsync("key") },
{ "KeyTypeAsync", () => db.KeyTypeAsync("key") },
{ "ListGetByIndexAsync", () => db.ListGetByIndexAsync("listkey", 0) },
{ "ListInsertAfterAsync", () => db.ListInsertAfterAsync("listkey", "value1", "value2") },
{ "ListInsertBeforeAsync", () => db.ListInsertBeforeAsync("listkey", "value1", "value2") },
{ "ListLeftPopAsync", () => db.ListLeftPopAsync("listkey") },
{ "ListLeftPushAsync", () => db.ListLeftPushAsync("listkey", new RedisValue[] { "value3", "value4" }) },
{ "ListLengthAsync", () => db.ListLengthAsync("listkey") },
{ "ListRangeAsync", () => db.ListRangeAsync("listkey") },
{ "ListRemoveAsync", () => db.ListRemoveAsync("listkey", "value3") },
{ "ListRightPopAsync", () => db.ListRightPopAsync("listkey") },
{ "ListRightPopLeftPushAsync", () => db.ListRightPopLeftPushAsync("listkey", "listkey2") },
{ "ListRightPushAsync", () => db.ListRightPushAsync("listkey", new RedisValue[] { "value5", "value6" }) },
{ "ListSetByIndexAsync", () => db.ListSetByIndexAsync("listkey", 0, "value7") },
{ "ListTrimAsync", () => db.ListTrimAsync("listkey", 0, 1) },
{ "LockExtendAsync", () => db.LockExtendAsync("listkey", "value7", new TimeSpan(0, 0, 10)) },
{ "LockQueryAsync", () => db.LockQueryAsync("listkey") },
{ "LockReleaseAsync", () => db.LockReleaseAsync("listkey", "value7") },
{ "LockTakeAsync", () => db.LockTakeAsync("listkey", "value8", new TimeSpan(0, 0, 10)) },
{ "PublishAsync", () => db.PublishAsync(new RedisChannel("channel", RedisChannel.PatternMode.Auto), "somemessage") },
// { "ScriptEvaluateAsync", () => db.ScriptEvaluateAsync(}
{ "SetAddAsync", () => db.SetAddAsync("setkey", "value1") },
{ "SetCombineAndStoreAsync", () => db.SetCombineAndStoreAsync(SetOperation.Union, "setkey", new RedisKey[] { "value2" }) },
{ "SetCombineAsync", () => db.SetCombineAsync(SetOperation.Union, new RedisKey[] { "setkey1", "setkey2"}) },
{ "SetContainsAsync", () => db.SetContainsAsync("setkey", "value1") },
{ "SetLengthAsync", () => db.SetLengthAsync("setkey") },
{ "SetMembersAsync", () => db.SetMembersAsync("setkey") },
{ "SetMoveAsync", () => db.SetMoveAsync("setkey1", "setkey2", "value2") },
{ "SetPopAsync", () => db.SetPopAsync("setkey1") },
{ "SetRandomMemberAsync", () => db.SetRandomMemberAsync("setkey") },
{ "SetRandomMembersAsync", () => db.SetRandomMembersAsync("setkey", 2) },
{ "SetRemoveAsync", () => db.SetRemoveAsync("setkey", "value2") },
{ "SortAndStoreAsync", () => db.SortAndStoreAsync("setkey2", "setkey") },
{ "SortAsync", () => db.SortAsync("setkey") },
{ "SortedSetAddAsync", () => db.SortedSetAddAsync("ssetkey", new SortedSetEntry[] { new SortedSetEntry("value1", 1.5), new SortedSetEntry("value2", 2.5) }) },
{ "SortedSetCombineAndStoreAsync", () => db.SortedSetCombineAndStoreAsync(SetOperation.Union, "ssetkey1", "ssetkey2", "ssetkey3") },
{ "SortedSetDecrementAsync", () => db.SortedSetDecrementAsync("ssetkey", "value1", 1) },
{ "SortedSetIncrementAsync", () => db.SortedSetIncrementAsync("ssetkey", "value2", 1) },
{ "SortedSetLengthAsync", () => db.SortedSetLengthAsync("ssetkey") },
{ "SortedSetLengthByValueAsync", () => db.SortedSetLengthByValueAsync("ssetkey", "value1", "value2") },
{ "SortedSetRangeByRankAsync", () => db.SortedSetRangeByRankAsync("ssetkey") },
{ "SortedSetRangeByRankWithScoresAsync", () => db.SortedSetRangeByRankWithScoresAsync("ssetkey") },
{ "SortedSetRangeByScoreAsync", () => db.SortedSetRangeByScoreAsync("ssetkey") },
{ "SortedSetRangeByScoreWithScoresAsync", () => db.SortedSetRangeByScoreWithScoresAsync("ssetkey") },
{ "SortedSetRangeByValueAsync", () => db.SortedSetRangeByValueAsync("ssetkey") },
{ "SortedSetRankAsync", () => db.SortedSetRankAsync("ssetkey", "value1") },
{ "SortedSetRemoveAsync", () => db.SortedSetRemoveAsync("ssetkey", "value1") },
{ "SortedSetRemoveRangeByRankAsync", () => db.SortedSetRemoveRangeByRankAsync("ssetkey", 0, 1) },
{ "SortedSetRemoveRangeByScoreAsync", () => db.SortedSetRemoveRangeByScoreAsync("ssetkey", 0, 1) },
{ "SortedSetRemoveRangeByValueAsync", () => db.SortedSetRemoveRangeByValueAsync("ssetkey", "value1", "value2") },
{ "SortedSetScoreAsync", () => db.SortedSetScoreAsync("ssestkey", "value1") },
{ "StringAppendAsync", () => db.StringAppendAsync("ssetkey", "value1") },
{ "StringBitCountAsync", () => db.StringBitCountAsync("ssetkey") },
{ "StringBitOperationAsync", () => db.StringBitOperationAsync(Bitwise.And, "ssetkey1", new RedisKey[] { "ssetkey2", "ssetkey3" }) },
{ "StringBitPositionAsync", () => db.StringBitPositionAsync("ssetkey1", true) },
{ "StringDecrementAsync", () => db.StringDecrementAsync("key", 1.45) },
{ "StringGetAsync", () => db.StringGetAsync("key") },
{ "StringGetBitAsync", () => db.StringGetBitAsync("key", 3) },
{ "StringGetRangeAsync", () => db.StringGetRangeAsync("key", 0, 1) },
{ "StringGetSetAsync", () => db.StringGetSetAsync("key", "value") },
{ "StringGetWithExpiryAsync", () => db.StringGetWithExpiryAsync("key") },
{ "StringIncrementAsync", () => db.StringIncrementAsync("key", 1) },
{ "StringLengthAsync", () => db.StringLengthAsync("key") },
{ "StringSetAsync", () => db.StringSetAsync(new KeyValuePair<RedisKey, RedisValue>[] { new KeyValuePair<RedisKey, RedisValue>("key", "value")}) },
{ "StringSetBitAsync", () => db.StringSetBitAsync("key", 0, true) },
{ "StringSetRangeAsync", () => db.StringSetRangeAsync("key", 3, "value") },
};

var pending = new TupleList<string, Task>();
foreach (var item in tasks)
{
try
{
pending.Add(item.Item1, item.Item2());
}
catch(Exception e)
{
while (e.InnerException != null)
{
e = e.InnerException;
}
Console.WriteLine($"{e.Message}");
}
}

return pending;
}

private static object TaskResult(Task task)
{
var taskType = task.GetType();

bool isTaskOfT =
taskType.IsGenericType
&& taskType.GetGenericTypeDefinition() == typeof(Task<>);

if (isTaskOfT)
{
return taskType.GetProperty("Result").GetValue(task);
}
else
{
task.Wait();
return null;
}
}

private static void RunCommands(TupleList<string, Func<object>> commands)
{
foreach (var cmd in commands)
{
if (cmd.Item2 == null)
{
continue;
}

object result;
try
{
Expand Down
7 changes: 5 additions & 2 deletions src/Datadog.Trace.ClrProfiler.Managed/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ private static Task<T> TraceTask<T>(Span span, Task<T> task, Action<Exception> o
{
if (t.IsFaulted)
{
onComplete(t.Exception);
throw t.Exception;
t.Exception.Handle(e =>
{
onComplete(e);
return false;
});
}

if (t.IsCompleted)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Linq;
using System.Reflection;

namespace Datadog.Trace.ClrProfiler.Integrations.StackExchange.Redis
{
/// <summary>
/// Base class for redis integration.
/// </summary>
public class Base
{
private static Func<object, string> _getCommandAndKeyMethod;

private static Func<object, string> _getConfigurationMethod;

/// <summary>
/// Get the configuration for the multiplexer.
/// </summary>
/// <param name="multiplexer">The multiplexer</param>
/// <returns>The configuration</returns>
protected static string GetConfiguration(object multiplexer)
{
try
{
if (_getConfigurationMethod == null)
{
_getConfigurationMethod = DynamicMethodBuilder<Func<object, string>>.CreateMethodCallDelegate(multiplexer.GetType(), "get_Configuration");
}

return _getConfigurationMethod(multiplexer);
}
catch
{
return null;
}
}

/// <summary>
/// Get the host and port from the config
/// </summary>
/// <param name="config">The config</param>
/// <returns>The host and port</returns>
protected static Tuple<string, string> GetHostAndPort(string config)
{
string host = null;
string port = null;

if (config != null)
{
// config can contain several settings separated by commas:
// hostname:port,name=MyName,keepAlive=180,syncTimeout=10000,abortConnect=False
// split in commas, find the one without '=', split that one on ':'
string[] hostAndPort = config.Split(',')
.FirstOrDefault(p => !p.Contains("="))
?.Split(':');

if (hostAndPort != null)
{
host = hostAndPort[0];
}

// check length because port is optional
if (hostAndPort?.Length > 1)
{
port = hostAndPort[1];
}
}

return new Tuple<string, string>(host, port);
}

/// <summary>
/// Get the raw command.
/// </summary>
/// <param name="multiplexer">The multiplexer</param>
/// <param name="message">The message</param>
/// <returns>The raw command</returns>
protected static string GetRawCommand(object multiplexer, object message)
{
string cmdAndKey = null;
try
{
if (_getCommandAndKeyMethod == null)
{
var asm = multiplexer.GetType().Assembly;
var messageType = asm.GetType("StackExchange.Redis.Message");
_getCommandAndKeyMethod = DynamicMethodBuilder<Func<object, string>>.CreateMethodCallDelegate(messageType, "get_CommandAndKey");
}

cmdAndKey = _getCommandAndKeyMethod(message);
}
catch
{
}

return cmdAndKey ?? "COMMAND";
}

/// <summary>
/// GetMultiplexer returns the Multiplexer for an object
/// </summary>
/// <param name="obj">The object</param>
/// <returns>The multiplexer</returns>
protected static object GetMultiplexer(object obj)
{
object multiplexer = null;
try
{
var fi = obj.GetType().GetField("multiplexer", BindingFlags.NonPublic | BindingFlags.Instance);
multiplexer = fi.GetValue(obj);
}
catch
{
}

return multiplexer;
}
}
}
Loading