Skip to content

Commit f6e0809

Browse files
committed
Feat: add Redis governance identifier set loading
1 parent 8825f8a commit f6e0809

4 files changed

Lines changed: 142 additions & 0 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using ModularityKit.Mutator.Governance.Redis.Storage.Identifiers.Models;
2+
using StackExchange.Redis;
3+
4+
namespace ModularityKit.Mutator.Governance.Redis.Storage.Identifiers.Loading;
5+
6+
/// <summary>
7+
/// Provides higher level Redis request id set reads for candidate execution.
8+
/// </summary>
9+
internal sealed class RedisMutationRequestIdSetReader(
10+
RedisMutationRequestIdentifierSetLoader setLoader)
11+
{
12+
private readonly RedisMutationRequestIdentifierSetLoader _setLoader = setLoader ?? throw new ArgumentNullException(nameof(setLoader));
13+
14+
/// <summary>
15+
/// Loads request identifiers from a single Redis set.
16+
/// </summary>
17+
/// <param name="key">The Redis set key.</param>
18+
/// <param name="cancellationToken">The cancellation token.</param>
19+
/// <returns>The resolved request identifiers.</returns>
20+
public async Task<IReadOnlyList<string>> LoadIdsAsync(RedisKey key, CancellationToken cancellationToken) =>
21+
await _setLoader.LoadAsync(RedisMutationRequestIdentifierSetOperation.Members, [key], cancellationToken)
22+
.ConfigureAwait(false);
23+
24+
/// <summary>
25+
/// Loads request identifiers from the union of the supplied Redis sets.
26+
/// </summary>
27+
/// <param name="keys">The Redis set keys to union.</param>
28+
/// <param name="cancellationToken">The cancellation token.</param>
29+
/// <returns>The resolved request identifiers.</returns>
30+
public async Task<IReadOnlyList<string>> LoadUnionedIdsAsync(IReadOnlyList<RedisKey> keys, CancellationToken cancellationToken) =>
31+
await _setLoader.LoadAsync(RedisMutationRequestIdentifierSetOperation.Union, keys, cancellationToken)
32+
.ConfigureAwait(false);
33+
34+
/// <summary>
35+
/// Loads request identifiers from the intersection of two Redis sets.
36+
/// </summary>
37+
/// <param name="left">The left Redis set key.</param>
38+
/// <param name="right">The right Redis set key.</param>
39+
/// <param name="cancellationToken">The cancellation token.</param>
40+
/// <returns>The resolved request identifiers.</returns>
41+
public async Task<IReadOnlyList<string>> LoadIntersectedIdsAsync(RedisKey left, RedisKey right, CancellationToken cancellationToken) =>
42+
await _setLoader.LoadAsync(RedisMutationRequestIdentifierSetOperation.Intersection, [left, right], cancellationToken)
43+
.ConfigureAwait(false);
44+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using ModularityKit.Mutator.Governance.Redis.Storage.Identifiers.Models;
2+
using StackExchange.Redis;
3+
4+
namespace ModularityKit.Mutator.Governance.Redis.Storage.Identifiers.Loading;
5+
6+
/// <summary>
7+
/// Executes low-level Redis set operations used to resolve request identifiers.
8+
/// </summary>
9+
internal sealed class RedisMutationRequestIdentifierSetLoader(IDatabase database)
10+
{
11+
private readonly IDatabase _database = database ?? throw new ArgumentNullException(nameof(database));
12+
13+
/// <summary>
14+
/// Loads request identifiers using the supplied Redis set operation.
15+
/// </summary>
16+
/// <param name="operation">The Redis set operation to execute.</param>
17+
/// <param name="keys">The Redis set keys participating in the operation.</param>
18+
/// <param name="cancellationToken">The cancellation token.</param>
19+
/// <returns>The normalized request identifiers.</returns>
20+
public async Task<IReadOnlyList<string>> LoadAsync(RedisMutationRequestIdentifierSetOperation operation, IReadOnlyList<RedisKey> keys, CancellationToken cancellationToken)
21+
{
22+
cancellationToken.ThrowIfCancellationRequested();
23+
24+
if (keys.Count == 0)
25+
return [];
26+
27+
var values = operation switch
28+
{
29+
RedisMutationRequestIdentifierSetOperation.Members => await _database
30+
.SetMembersAsync(keys[0])
31+
.ConfigureAwait(false),
32+
RedisMutationRequestIdentifierSetOperation.Union => await LoadCombinedAsync(
33+
SetOperation.Union,
34+
keys,
35+
cancellationToken).ConfigureAwait(false),
36+
RedisMutationRequestIdentifierSetOperation.Intersection => await LoadCombinedAsync(
37+
SetOperation.Intersect,
38+
keys,
39+
cancellationToken).ConfigureAwait(false),
40+
_ => throw new InvalidOperationException($"Unsupported identifier set operation '{operation}'.")
41+
};
42+
43+
return RedisMutationRequestIdentifierValueNormalizer.Normalize(values);
44+
}
45+
46+
private async Task<RedisValue[]> LoadCombinedAsync(SetOperation operation, IReadOnlyList<RedisKey> keys, CancellationToken cancellationToken)
47+
{
48+
cancellationToken.ThrowIfCancellationRequested();
49+
50+
if (keys.Count == 1)
51+
return await _database.SetMembersAsync(keys[0]).ConfigureAwait(false);
52+
53+
return await _database.SetCombineAsync(operation, keys.ToArray()).ConfigureAwait(false);
54+
}
55+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using StackExchange.Redis;
2+
3+
namespace ModularityKit.Mutator.Governance.Redis.Storage.Identifiers.Loading;
4+
5+
/// <summary>
6+
/// Normalizes raw Redis set members into stable request-id lists.
7+
/// </summary>
8+
internal static class RedisMutationRequestIdentifierValueNormalizer
9+
{
10+
/// <summary>
11+
/// Converts Redis values into distinct, non-empty request identifiers.
12+
/// </summary>
13+
/// <param name="values">The Redis values to normalize.</param>
14+
/// <returns>The normalized request identifiers.</returns>
15+
public static IReadOnlyList<string> Normalize(RedisValue[] values) =>
16+
values.Where(value => value.HasValue)
17+
.Select(value => value.ToString())
18+
.Where(value => !string.IsNullOrWhiteSpace(value))
19+
.Distinct(StringComparer.Ordinal)
20+
.ToArray();
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace ModularityKit.Mutator.Governance.Redis.Storage.Identifiers.Models;
2+
3+
/// <summary>
4+
/// Defines the Redis set operation used to resolve request identifiers.
5+
/// </summary>
6+
internal enum RedisMutationRequestIdentifierSetOperation
7+
{
8+
/// <summary>
9+
/// Reads the members of a single Redis set.
10+
/// </summary>
11+
Members = 0,
12+
13+
/// <summary>
14+
/// Reads the union of multiple Redis sets.
15+
/// </summary>
16+
Union = 1,
17+
18+
/// <summary>
19+
/// Reads the intersection of multiple Redis sets.
20+
/// </summary>
21+
Intersection = 2
22+
}

0 commit comments

Comments
 (0)