Skip to content

Added async set operations. #1

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

Merged
merged 1 commit into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
187 changes: 186 additions & 1 deletion IntegrationTests/RedisTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;

namespace PubComp.RedisRepo.IntegrationTests
{
Expand Down Expand Up @@ -76,7 +77,7 @@ public static void Retry(Action action, int maxAttempts)
return RetryUtil.Retry(() => this.Database.KeyTimeToLive(key), 3);
}
}

#endregion

#region Test Cases
Expand Down Expand Up @@ -306,6 +307,91 @@ public void SetOperations_Double()
.OrderBy(x => x).ToList());
}

[TestMethod]
public async Task AsyncSetOperations_Parallel()
{
var key1 = TestContext.TestName + ".1";

redisContext.Delete(key1);

var range = Enumerable.Range(1, 10).ToList();

var tasks = range.Select(i => redisContext.SetAddAsync<int>(key1, i));
await Task.WhenAll(tasks);

var set = (await redisContext.SetGetItemsAsync(key1, RedisValueConverter.ToInt))
.OrderBy(x => x).ToList();

CollectionAssert.AreEquivalent(range, set);
}

[TestMethod]
public async Task AsyncSetOperations_Double()
{
var key1 = TestContext.TestName + ".1";
var key2 = TestContext.TestName + ".2";
var key3 = TestContext.TestName + ".3";

redisContext.Delete(key1);
redisContext.Delete(key2);
redisContext.Delete(key3);

await redisContext.SetAddAsync<double>(key1, new[] { 5.0, 2.0, 1.5 });
await redisContext.SetAddAsync<double>(key1, 3.5);

CollectionAssert.AreEquivalent(
new[] { 1.5, 2.0, 3.5, 5.0 },
(await redisContext.SetGetItemsAsync(key1, RedisValueConverter.ToDouble))
.OrderBy(x => x).ToList());

await redisContext.SetAddAsync<double>(key2, new[] { 7.0, 4.0, 1.5 });
await redisContext.SetAddAsync<double>(key3, new[] { 1.5, 7.0, 3.5, 8.5 });

var actualIntersect123 = await redisContext.SetsIntersectAsync(
new[] { key1, key2, key3 }, RedisValueConverter.ToDouble);
CollectionAssert.AreEquivalent(
new[] { 1.5 },
actualIntersect123.OrderBy(x => x).ToList());

var actualUnion123 = await redisContext.SetsUnionAsync(
new[] { key1, key2, key3 }, RedisValueConverter.ToDouble);
CollectionAssert.AreEquivalent(
new[] { 1.5, 2.0, 3.5, 4.0, 5.0, 7.0, 8.5 },
actualUnion123.OrderBy(x => x).ToList());

var actualMinus123 = await redisContext.SetsDiffAsync(
new[] { key1, key2, key3 }, RedisValueConverter.ToDouble);
CollectionAssert.AreEquivalent(
new[] { 2.0, 5.0 },
actualMinus123.OrderBy(x => x).ToList());

Assert.AreEqual(4, await redisContext.SetLengthAsync(key1));
Assert.AreEqual(3, await redisContext.SetLengthAsync(key2));
Assert.AreEqual(4, await redisContext.SetLengthAsync(key3));

await redisContext.SetRemoveAsync<double>(key1, 2.0);
Assert.AreEqual(3, await redisContext.SetLengthAsync(key1));
CollectionAssert.AreEquivalent(
new[] { 1.5, 3.5, 5.0 },
(await redisContext.SetGetItemsAsync(key1, RedisValueConverter.ToDouble))
.OrderBy(x => x).ToList());

await redisContext.SetRemoveAsync<double>(key3, new[] { 2.0, 8.5, 7.0 });
Assert.AreEqual(2, await redisContext.SetLengthAsync(key3));
CollectionAssert.AreEquivalent(
new[] { 1.5, 3.5 },
(await redisContext.SetGetItemsAsync(key3, RedisValueConverter.ToDouble))
.OrderBy(x => x).ToList());

redisContext.Delete(key2);
await redisContext.SetAddAsync<double>(key2, 9.0);
Assert.AreEqual(1, await redisContext.SetLengthAsync(key2));
CollectionAssert.AreEquivalent(
new[] { 9.0 },
(await redisContext.SetGetItemsAsync(key2, RedisValueConverter.ToDouble))
.OrderBy(x => x).ToList());
}

[TestMethod]
public void SetDeleteMany()
{
Expand Down Expand Up @@ -694,18 +780,40 @@ public void TestAddToRedisSet()
ValidateSetResults(key, new[] { "a", "b", "c", "bar" });
}

[TestMethod]
public async Task TestAddToRedisSetAsync()
{
const string key = "k1";
var values = new[] { "bar", "bar", "a", "b", "c" };

await redisContext.AddToSetAsync(key, values);

await ValidateSetResultsAsync(key, new[] { "a", "b", "c", "bar" });
}

[TestMethod]
public void TestSetContainsTrue()
{
TestSetContains("a", "a", true);
}

[TestMethod]
public async Task TestSetContainsTrueAsync()
{
await TestSetContainsAsync("a", "a", true);
}

[TestMethod]
public void TestSetContainsFalse()
{
TestSetContains("a", "b", false);
}

[TestMethod]
public async Task TestSetContainsFalseAsync()
{
await TestSetContainsAsync("a", "b", false);
}

[TestMethod]
public void TestCountSetMembers()
Expand All @@ -718,6 +826,17 @@ public void TestCountSetMembers()
Assert.AreEqual(4, redisContext.CountSetMembers(key));
}

[TestMethod]
public async Task TestCountSetMembersAsync()
{
const string key = "k2";
var values = new[] { "bar", "bar", "a", "b", "c", "a", "b" };

await redisContext.AddToSetAsync(key, values);

Assert.AreEqual(4, await redisContext.CountSetMembersAsync(key));
}

[TestMethod]
public void TestSetsDiff()
{
Expand All @@ -734,6 +853,22 @@ public void TestSetsDiff()
CollectionAssert.AreEquivalent(new[] { "c", "d", "e" }, results);
}

[TestMethod]
public async Task TestSetsDiffAsync()
{
const string key1 = "testSetDiff1";
var values1 = new[] { "a", "b", "c", "d", "e" };

const string key2 = "testSetDiff2";
var values2 = new[] { "a", "b", };

await redisContext.AddToSetAsync(key1, values1);
await redisContext.AddToSetAsync(key2, values2);

var results = await redisContext.GetSetsDifferenceAsync(new[] { key1, key2 });
CollectionAssert.AreEquivalent(new[] { "c", "d", "e" }, results);
}

[TestMethod]
public void TestSetsUnion()
{
Expand All @@ -750,6 +885,22 @@ public void TestSetsUnion()
CollectionAssert.AreEquivalent(new[] { "a", "b", "c" }, results);
}

[TestMethod]
public async Task TestSetsUnionAsync()
{
const string key1 = "TestSetsUnion1";
var values1 = new[] { "a", "c" };

const string key2 = "TestSetsUnion2";
var values2 = new[] { "a", "b" };

await redisContext.AddToSetAsync(key1, values1);
await redisContext.AddToSetAsync(key2, values2);

var results = await redisContext.UnionSetsAsync(new[] { key1, key2 });
CollectionAssert.AreEquivalent(new[] { "a", "b", "c" }, results);
}

[TestMethod]
public void TestSetsIntersect()
{
Expand All @@ -766,12 +917,34 @@ public void TestSetsIntersect()
CollectionAssert.AreEquivalent(new[] { "a" }, results);
}

[TestMethod]
public async Task TestSetsIntersectAsync()
{
const string key1 = "TestSetsIntersect1";
var values1 = new[] { "a", "c" };

const string key2 = "TestSetsIntersect2";
var values2 = new[] { "a", "b" };

await redisContext.AddToSetAsync(key1, values1);
await redisContext.AddToSetAsync(key2, values2);

var results = await redisContext.IntersectSetsAsync(new[] { key1, key2 });
CollectionAssert.AreEquivalent(new[] { "a" }, results);
}

private void ValidateSetResults(string key, string[] expected)
{
var valuesFromRedis = redisContext.GetSetMembers(key);
CollectionAssert.AreEquivalent(expected, valuesFromRedis);
}

private async Task ValidateSetResultsAsync(string key, string[] expected)
{
var valuesFromRedis = await redisContext.GetSetMembersAsync(key);
CollectionAssert.AreEquivalent(expected, valuesFromRedis);
}

public void TestSetContains(string valueToAdd, string searchForValue, bool expected)
{
const string key = "testSetContains";
Expand All @@ -784,6 +957,18 @@ public void TestSetContains(string valueToAdd, string searchForValue, bool expec
Assert.AreEqual(expected, setContains);
}

public async Task TestSetContainsAsync(string valueToAdd, string searchForValue, bool expected)
{
const string key = "testSetContains";
var values = new[] { "foo", "bar" };

await redisContext.AddToSetAsync(key, values);
await redisContext.AddToSetAsync(key, new[] { valueToAdd });

var setContains = await redisContext.SetContainsAsync(key, searchForValue);
Assert.AreEqual(expected, setContains);
}

#endregion

#region Redis Hashes
Expand Down
47 changes: 47 additions & 0 deletions RedisRepo/IRedisContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using StackExchange.Redis;

namespace PubComp.RedisRepo
Expand Down Expand Up @@ -78,63 +79,109 @@ public interface IRedisContext
#region Redis Sets

bool SetAdd<T>(string key, T value);
Task<bool> SetAddAsync<T>(string key, T value);

long SetAdd<T>(string key, T[] values);
Task<long> SetAddAsync<T>(string key, T[] values);

bool SetRemove<T>(string key, T value);
Task<bool> SetRemoveAsync<T>(string key, T value);

long SetRemove<T>(string key, T[] values);
Task<long> SetRemoveAsync<T>(string key, T[] values);

long SetLength(string key);
Task<long> SetLengthAsync(string key);

T[] SetGetItems<T>(string key, Func<object, T> redisValueConverter);
Task<T[]> SetGetItemsAsync<T>(string key, Func<object, T> redisValueConverter);

T[] SetsUnion<T>(string[] keys, Func<object, T> redisValueConverter);
Task<T[]> SetsUnionAsync<T>(string[] keys, Func<object, T> redisValueConverter);

T[] SetsIntersect<T>(string[] keys, Func<object, T> redisValueConverter);
Task<T[]> SetsIntersectAsync<T>(string[] keys, Func<object, T> redisValueConverter);

T[] SetsDiff<T>(string[] keys, Func<object, T> redisValueConverter);
Task<T[]> SetsDiffAsync<T>(string[] keys, Func<object, T> redisValueConverter);

void AddToSet(string key, string[] values);
Task AddToSetAsync(string key, string[] values);

long CountSetMembers(string key);
Task<long> CountSetMembersAsync(string key);

string[] GetSetMembers(string key);
Task<string[]> GetSetMembersAsync(string key);

/// <summary>
/// Get the diff between the set at index 0 of <paramref name="keys"/> and all other sets in <paramref name="keys"/>
/// </summary>
string[] GetSetsDifference(string[] keys);

/// <summary>
/// Get the diff between the set at index 0 of <paramref name="keys"/> and all other sets in <paramref name="keys"/>
/// </summary>
Task<string[]> GetSetsDifferenceAsync(string[] keys);

/// <summary>
/// Union sets at keys <paramref name="setKeys"/>
/// </summary>
string[] UnionSets(string[] keys);

/// <summary>
/// Union sets at keys <paramref name="setKeys"/>
/// </summary>
Task<string[]> UnionSetsAsync(string[] keys);

/// <summary>
/// Intersect sets at keys <paramref name="keys"/>
/// </summary>
string[] IntersectSets(string[] keys);

/// <summary>
/// Intersect sets at keys <paramref name="keys"/>
/// </summary>
Task<string[]> IntersectSetsAsync(string[] keys);

/// <summary>
/// Get the diff between the set at index 0 of <paramref name="keys"/> and all other sets in <paramref name="keys"/>
/// store the result at <param name="destinationKey"></param>
/// </summary>
void StoreSetsDifference(string destinationKey, string[] keys);

/// <summary>
/// Get the diff between the set at index 0 of <paramref name="keys"/> and all other sets in <paramref name="keys"/>
/// store the result at <param name="destinationKey"></param>
/// </summary>
Task StoreSetsDifferenceAsync(string destinationKey, string[] keys);

/// <summary>
/// Union sets at keys <paramref name="keys"/>
/// store the result at <param name="destinationKey"></param>
/// </summary>
void UnionSetsAndStore(string destinationKey, string[] keys);

/// <summary>
/// Union sets at keys <paramref name="keys"/>
/// store the result at <param name="destinationKey"></param>
/// </summary>
Task UnionSetsAndStoreAsync(string destinationKey, string[] keys);

/// <summary>
/// Intersect sets at keys <paramref name="keys"/>
/// store the result at <param name="destinationKey"></param>
/// </summary>
void IntersectSetsAndStore(string destinationKey, string[] keys);

/// <summary>
/// Intersect sets at keys <paramref name="keys"/>
/// store the result at <param name="destinationKey"></param>
/// </summary>
Task IntersectSetsAndStoreAsync(string destinationKey, string[] keys);

bool SetContains(string key, string member);
Task<bool> SetContainsAsync(string key, string member);

bool TryGetDistributedLock(string lockObjectName, string lockerName, TimeSpan lockTtl);

Expand Down
Loading