Skip to content
Open
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
6 changes: 3 additions & 3 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ jobs:
fail-fast: false
matrix:
# Full version set runs only on the nightly schedule; PRs and pushes
# test a reduced set (7.2 LTS, 7.4 LTS, 8.2 LTS, 8.8, 8.10 current stable) to keep CI fast.
# test a reduced set (7.2 LTS, 7.4 LTS, 8.2 LTS, 8.8, 8.10 current stable, 8.12 preview) to keep CI fast.
redis-version: ${{ fromJSON(github.event_name == 'schedule'
&& '["8.10", "8.8", "8.6", "8.4", "8.2", "7.4", "7.2", "6.2"]'
|| '["8.10", "8.8", "8.2", "7.4", "7.2"]') }}
&& '["8.12", "8.10", "8.8", "8.6", "8.4", "8.2", "7.4", "7.2", "6.2"]'
|| '["8.12", "8.10", "8.8", "8.2", "7.4", "7.2"]') }}
dotnet-version: ['8.0', '9.0', '10.0']
name: Redis ${{ matrix.redis-version }}; .NET ${{ matrix.dotnet-version }};
steps:
Expand Down
25 changes: 25 additions & 0 deletions src/NRedisStack/CountMinSketch/CmsCommandBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,36 @@ public static SerializedCommand InitByDim(RedisKey key, long width, long depth)
return new(CommandCategories.WriteAccumulating, CMS.INITBYDIM, key, width, depth);
}

public static SerializedCommand InitByDim(RedisKey key, long width, long depth, int cellSize)
{
List<object> args = [key, width, depth];
AddCellSize(args, cellSize);

return new(CommandCategories.WriteAccumulating, CMS.INITBYDIM, args);
}

public static SerializedCommand InitByProb(RedisKey key, double error, double probability)
{
return new(CommandCategories.WriteAccumulating, CMS.INITBYPROB, key, error, probability);
}

public static SerializedCommand InitByProb(RedisKey key, double error, double probability, int cellSize)
{
List<object> args = [key, error, probability];
AddCellSize(args, cellSize);

return new(CommandCategories.WriteAccumulating, CMS.INITBYPROB, args);
}

private static void AddCellSize(List<object> args, int cellSize)
{
if (cellSize is not (1 or 2 or 4 or 8))
throw new ArgumentOutOfRangeException(nameof(cellSize), cellSize, "Cell size must be 1, 2, 4, or 8.");

args.Add(CmsArgs.CELL_SIZE);
args.Add(cellSize);
}

public static SerializedCommand Merge(RedisValue destination, long numKeys, RedisValue[] source,
long[]? weight = null)
{
Expand Down
12 changes: 12 additions & 0 deletions src/NRedisStack/CountMinSketch/CmsCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,24 @@ public bool InitByDim(RedisKey key, long width, long depth)
return _db.Execute(CmsCommandBuilder.InitByDim(key, width, depth)).OKtoBoolean();
}

/// <inheritdoc/>
public bool InitByDim(RedisKey key, long width, long depth, int cellSize)
{
return _db.Execute(CmsCommandBuilder.InitByDim(key, width, depth, cellSize)).OKtoBoolean();
}

/// <inheritdoc/>
public bool InitByProb(RedisKey key, double error, double probability)
{
return _db.Execute(CmsCommandBuilder.InitByProb(key, error, probability)).OKtoBoolean();
}

/// <inheritdoc/>
public bool InitByProb(RedisKey key, double error, double probability, int cellSize)
{
return _db.Execute(CmsCommandBuilder.InitByProb(key, error, probability, cellSize)).OKtoBoolean();
}

/// <inheritdoc/>
public bool Merge(RedisValue destination, long numKeys, RedisValue[] source, long[]? weight = null)
{
Expand Down
12 changes: 12 additions & 0 deletions src/NRedisStack/CountMinSketch/CmsCommandsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,24 @@ public async Task<bool> InitByDimAsync(RedisKey key, long width, long depth)
return (await _db.ExecuteAsync(CmsCommandBuilder.InitByDim(key, width, depth))).OKtoBoolean();
}

/// <inheritdoc/>
public async Task<bool> InitByDimAsync(RedisKey key, long width, long depth, int cellSize)
{
return (await _db.ExecuteAsync(CmsCommandBuilder.InitByDim(key, width, depth, cellSize))).OKtoBoolean();
}

/// <inheritdoc/>
public async Task<bool> InitByProbAsync(RedisKey key, double error, double probability)
{
return (await _db.ExecuteAsync(CmsCommandBuilder.InitByProb(key, error, probability))).OKtoBoolean();
}

/// <inheritdoc/>
public async Task<bool> InitByProbAsync(RedisKey key, double error, double probability, int cellSize)
{
return (await _db.ExecuteAsync(CmsCommandBuilder.InitByProb(key, error, probability, cellSize))).OKtoBoolean();
}

/// <inheritdoc/>
public async Task<bool> MergeAsync(RedisValue destination, long numKeys, RedisValue[] source, long[]? weight = null)
{
Expand Down
23 changes: 23 additions & 0 deletions src/NRedisStack/CountMinSketch/ICmsCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public interface ICmsCommands
/// <remarks><seealso href="https://redis.io/commands/cms.initbydim"/></remarks>
bool InitByDim(RedisKey key, long width, long depth);

/// <summary>
/// Initializes a Count-Min Sketch to dimensions specified by user.
/// </summary>
/// <param name="key">TThe name of the sketch.</param>
/// <param name="width">Number of counters in each array. Reduces the error size.</param>
/// <param name="depth">Number of counter-arrays. Reduces the probability for an error
/// of a certain size (percentage of total count).</param>
/// <param name="cellSize">The number of bytes per cell (1, 2, 4, or 8).</param>
/// <returns><see langword="true"/> if if executed correctly, Error otherwise.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.initbydim"/></remarks>
bool InitByDim(RedisKey key, long width, long depth, int cellSize);

/// <summary>
/// Initializes a Count-Min Sketch to accommodate requested tolerances.
/// </summary>
Expand All @@ -54,6 +66,17 @@ public interface ICmsCommands
/// <remarks><seealso href="https://redis.io/commands/cms.initbyprob"/></remarks>
bool InitByProb(RedisKey key, double error, double probability);

/// <summary>
/// Initializes a Count-Min Sketch to accommodate requested tolerances.
/// </summary>
/// <param name="key">The name of the sketch.</param>
/// <param name="error">Estimate size of error.</param>
/// <param name="probability">The desired probability for inflated count.</param>
/// <param name="cellSize">The number of bytes per cell (1, 2, 4, or 8).</param>
/// <returns><see langword="true"/> if if executed correctly, Error otherwise.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.initbyprob"/></remarks>
bool InitByProb(RedisKey key, double error, double probability, int cellSize);

/// <summary>
/// Merges several sketches into one sketch.
/// </summary>
Expand Down
23 changes: 23 additions & 0 deletions src/NRedisStack/CountMinSketch/ICmsCommandsAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public interface ICmsCommandsAsync
/// <remarks><seealso href="https://redis.io/commands/cms.initbydim"/></remarks>
Task<bool> InitByDimAsync(RedisKey key, long width, long depth);

/// <summary>
/// Initializes a Count-Min Sketch to dimensions specified by user.
/// </summary>
/// <param name="key">TThe name of the sketch.</param>
/// <param name="width">Number of counters in each array. Reduces the error size.</param>
/// <param name="depth">Number of counter-arrays. Reduces the probability for an error
/// of a certain size (percentage of total count).</param>
/// <param name="cellSize">The number of bytes per cell (1, 2, 4, or 8).</param>
/// <returns><see langword="true"/> if if executed correctly, Error otherwise.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.initbydim"/></remarks>
Task<bool> InitByDimAsync(RedisKey key, long width, long depth, int cellSize);

/// <summary>
/// Initializes a Count-Min Sketch to accommodate requested tolerances.
/// </summary>
Expand All @@ -54,6 +66,17 @@ public interface ICmsCommandsAsync
/// <remarks><seealso href="https://redis.io/commands/cms.initbyprob"/></remarks>
Task<bool> InitByProbAsync(RedisKey key, double error, double probability);

/// <summary>
/// Initializes a Count-Min Sketch to accommodate requested tolerances.
/// </summary>
/// <param name="key">The name of the sketch.</param>
/// <param name="error">Estimate size of error.</param>
/// <param name="probability">The desired probability for inflated count.</param>
/// <param name="cellSize">The number of bytes per cell (1, 2, 4, or 8).</param>
/// <returns><see langword="true"/> if if executed correctly, Error otherwise.</returns>
/// <remarks><seealso href="https://redis.io/commands/cms.initbyprob"/></remarks>
Task<bool> InitByProbAsync(RedisKey key, double error, double probability, int cellSize);

/// <summary>
/// Merges several sketches into one sketch.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/NRedisStack/CountMinSketch/Literals/CommandArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ namespace NRedisStack.CountMinSketch.Literals;
internal class CmsArgs
{
public const string WEIGHTS = "WEIGHTS";
public const string CELL_SIZE = "CELL_SIZE";
}
10 changes: 10 additions & 0 deletions src/NRedisStack/PublicAPI/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
#nullable enable
NRedisStack.CountMinSketch.DataTypes.CmsInformation.CellSize.get -> int
NRedisStack.CmsCommands.InitByDim(StackExchange.Redis.RedisKey key, long width, long depth, int cellSize) -> bool
NRedisStack.CmsCommands.InitByProb(StackExchange.Redis.RedisKey key, double error, double probability, int cellSize) -> bool
NRedisStack.CmsCommandsAsync.InitByDimAsync(StackExchange.Redis.RedisKey key, long width, long depth, int cellSize) -> System.Threading.Tasks.Task<bool>!
NRedisStack.CmsCommandsAsync.InitByProbAsync(StackExchange.Redis.RedisKey key, double error, double probability, int cellSize) -> System.Threading.Tasks.Task<bool>!
NRedisStack.ICmsCommands.InitByDim(StackExchange.Redis.RedisKey key, long width, long depth, int cellSize) -> bool
NRedisStack.ICmsCommands.InitByProb(StackExchange.Redis.RedisKey key, double error, double probability, int cellSize) -> bool
NRedisStack.ICmsCommandsAsync.InitByDimAsync(StackExchange.Redis.RedisKey key, long width, long depth, int cellSize) -> System.Threading.Tasks.Task<bool>!
NRedisStack.ICmsCommandsAsync.InitByProbAsync(StackExchange.Redis.RedisKey key, double error, double probability, int cellSize) -> System.Threading.Tasks.Task<bool>!
static NRedisStack.CmsCommandBuilder.InitByDim(StackExchange.Redis.RedisKey key, long width, long depth, int cellSize) -> NRedisStack.RedisStackCommands.SerializedCommand!
static NRedisStack.CmsCommandBuilder.InitByProb(StackExchange.Redis.RedisKey key, double error, double probability, int cellSize) -> NRedisStack.RedisStackCommands.SerializedCommand!
2 changes: 1 addition & 1 deletion src/NRedisStack/ResponseParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ public static CmsInformation
case "count":
count = (long)redisResults[i];
break;
case "cell size":
case "cell_size":
cellSize = (int)redisResults[i];
break;
}
Expand Down
90 changes: 90 additions & 0 deletions tests/NRedisStack.Tests/CountMinSketch/CmsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,48 @@ public async Task TestInitByDimAsync(string endpointId)
Assert.Equal(0, info.Count);
}

[SkipIfRedisTheory(Comparison.LessThan, "8.12.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestInitByDimCellSize(string endpointId)
{
var db = GetCleanDatabase(endpointId);
var cms = db.CMS();

cms.InitByDim(key, 16, 4, cellSize: 1);
Comment thread
mgravell marked this conversation as resolved.
var info = cms.Info(key);

Assert.Equal(16, info.Width);
Assert.Equal(4, info.Depth);
Assert.Equal(0, info.Count);
Assert.Equal(1, info.CellSize);
}

[SkipIfRedisTheory(Comparison.LessThan, "8.12.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public async Task TestInitByDimCellSizeAsync(string endpointId)
{
var db = GetCleanDatabase(endpointId);
var cms = db.CMS();

await cms.InitByDimAsync(key, 16, 4, cellSize: 8);
var info = await cms.InfoAsync(key);

Assert.Equal(16, info.Width);
Assert.Equal(4, info.Depth);
Assert.Equal(0, info.Count);
Assert.Equal(8, info.CellSize);
}
Comment thread
cursor[bot] marked this conversation as resolved.

[Theory]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestInitByDimInvalidCellSize(string endpointId)
{
var db = GetCleanDatabase(endpointId);
var cms = db.CMS();

Assert.Throws<ArgumentOutOfRangeException>(() => cms.InitByDim(key, 16, 4, cellSize: 3));
}

[Theory]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestInitByProb(string endpointId)
Expand All @@ -54,6 +96,22 @@ public void TestInitByProb(string endpointId)
Assert.Equal(0, info.Count);
}

[SkipIfRedisTheory(Comparison.LessThan, "8.12.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestInitByProbCellSize(string endpointId)
{
var db = GetCleanDatabase(endpointId);
var cms = db.CMS();

cms.InitByProb(key, 0.01, 0.01, cellSize: 2);
var info = cms.Info(key);

Assert.Equal(200, info.Width);
Assert.Equal(7, info.Depth);
Assert.Equal(0, info.Count);
Assert.Equal(2, info.CellSize);
}

[Theory]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public async Task TestInitByProbAsync(string endpointId)
Expand Down Expand Up @@ -127,6 +185,38 @@ public async Task TestIncrByAsync(string endpointId)

}

[SkipIfRedisTheory(Comparison.LessThan, "8.12.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestIncrByNegative(string endpointId)
{
var db = GetCleanDatabase(endpointId);
var cms = db.CMS();

cms.InitByDim(key, 1000, 5);
cms.IncrBy(key, "foo", 10);
var resp = cms.IncrBy(key, "foo", -4);
Comment thread
mgravell marked this conversation as resolved.
Assert.Equal(6, resp);

var info = cms.Info(key);
Assert.Equal(6, info.Count);
}

[SkipIfRedisTheory(Comparison.LessThan, "8.12.0")]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public async Task TestIncrByNegativeAsync(string endpointId)
{
var db = GetCleanDatabase(endpointId);
var cms = db.CMS();

await cms.InitByDimAsync(key, 1000, 5);
await cms.IncrByAsync(key, "foo", 10);
var resp = await cms.IncrByAsync(key, "foo", -4);
Assert.Equal(6, resp);

var info = await cms.InfoAsync(key);
Assert.Equal(6, info.Count);
}

[Theory]
[MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))]
public void TestIncrByMultipleArgs(string endpointId)
Expand Down
7 changes: 7 additions & 0 deletions tests/dockers/.env.v8.12
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Environment variables for Redis 8.12
# Used by the run-tests GitHub Action

# No official 8.12 release image exists yet, so this is pinned to an unstable
# preview build. Unlike the other .env.v* files, this tag is not guaranteed to
# stay available - replace with the real release image once 8.12 ships.
CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:unstable-34786335206-debian
Loading