Skip to content

Commit

Permalink
feat(redis): enhance key deletion and add prefix retrieval
Browse files Browse the repository at this point in the history
- Modified the `DeleteAllWithPatternBatchedScript` to use `KEYS[1]` instead of `ARGV[1]`.
- Changed the batch size parameter type from uint to int in `KeysDelete` and `KeysDeleteAsync`.
- Added a new script, `DeterminePrefixScript`, to determine the key prefix.
- Implemented two new methods, `GetKeyPrefix` and `GetKeyPrefixAsync`, for retrieving the key prefix.
- Introduced two additional methods, `KeysDeleteAll` and `KeysDeleteAllAsync`, for deleting all keys.
- Updated unit tests in DatabaseExtensionTests.cs to reflect these changes.
  • Loading branch information
winromulus committed Sep 22, 2024
1 parent 2de38b4 commit 5e8b62a
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#pragma warning disable CS9113 // Parameter is unread.

using ES.FX.StackExchange.Redis;
using ES.FX.TransactionalOutbox.EntityFrameworkCore;
using ES.FX.TransactionalOutbox.EntityFrameworkCore.Messages;
using Microsoft.EntityFrameworkCore;
using Playground.Microservice.Api.Host.Testing;
using Playground.Shared.Data.Simple.EntityFrameworkCore;
using Playground.Shared.Data.Simple.EntityFrameworkCore.Entities;
using StackExchange.Redis;
using StackExchange.Redis.KeyspaceIsolation;

namespace Playground.Microservice.Api.Host.HostedServices;

Expand All @@ -17,7 +20,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.CompletedTask;


var factory = serviceProvider.GetRequiredService<IDbContextFactory<SimpleDbContext>>();
while (true)
{
Expand Down
58 changes: 50 additions & 8 deletions src/ES.FX.StackExchange.Redis/DatabaseExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public static class DatabaseExtensions
/// </summary>
private const string DeleteAllWithPatternBatchedScript = @"
local cursor = '0'
local batchSize = tonumber(ARGV[2])
local batchSize = tonumber(ARGV[1])
local totalDeleted = 0
local keys
repeat
keys = {}
local result = redis.call('SCAN', cursor, 'MATCH', ARGV[1], 'COUNT', batchSize)
local result = redis.call('SCAN', cursor, 'MATCH', KEYS[1], 'COUNT', batchSize)
cursor = result[1]
for i, key in ipairs(result[2]) do
table.insert(keys, key)
Expand All @@ -32,16 +32,37 @@ local keys
return totalDeleted
";

private const string DeterminePrefixScript =
"return string.sub(KEYS[1],1,string.len(KEYS[1])-string.len(ARGV[1]))";


/// <summary>
/// Attempts to get the key prefix.
/// </summary>
/// <param name="database">The <see cref="IDatabase" /></param>
public static RedisResult GetKeyPrefix(this IDatabase database) =>
database.ScriptEvaluateReadOnly(DeterminePrefixScript, [nameof(Redis)], [nameof(Redis)]);

/// <summary>
/// Attempts to get the key prefix.
/// </summary>
/// <param name="database">The <see cref="IDatabase" /></param>
public static async Task<RedisResult> GetKeyPrefixAsync(this IDatabase database) =>
await database.ScriptEvaluateReadOnlyAsync(DeterminePrefixScript, [nameof(Redis)], [nameof(Redis)]);


/// <summary>
/// Deletes all keys matching a pattern in batches
/// </summary>
/// <param name="database">The <see cref="IDatabase" /></param>
/// <param name="pattern">Pattern to MATCH keys</param>
/// <param name="batchSize">The batch size</param>
/// <returns>The deleted key count</returns>
public static long KeysDelete(this IDatabase database, string pattern, uint batchSize = 1000)
public static long KeysDelete(this IDatabase database, string pattern, int batchSize = 1000)
{
var result = database.ScriptEvaluate(DeleteAllWithPatternBatchedScript, values: [pattern, batchSize]);
var result = database.ScriptEvaluate(
DeleteAllWithPatternBatchedScript,
[pattern], [batchSize]);
return long.Parse(result.ToString());
}

Expand All @@ -52,11 +73,32 @@ public static long KeysDelete(this IDatabase database, string pattern, uint batc
/// <param name="pattern">Pattern to MATCH keys</param>
/// <param name="batchSize">The batch size</param>
/// <returns>The deleted key count</returns>
public static async Task<long> KeysDeleteAsync(this IDatabase database, string pattern, uint batchSize = 1000)
public static async Task<long> KeysDeleteAsync(this IDatabase database, string pattern, int batchSize = 1000)
{
var result =
await database.ScriptEvaluateAsync(DeleteAllWithPatternBatchedScript, values: [pattern, batchSize])
.ConfigureAwait(false);
var result = await database.ScriptEvaluateAsync(
DeleteAllWithPatternBatchedScript,
[pattern], [batchSize])
.ConfigureAwait(false);
return long.Parse(result.ToString());
}


/// <summary>
/// Deletes all keys
/// </summary>
/// <param name="database">The <see cref="IDatabase" /></param>
/// <param name="batchSize">The batch size</param>
/// <returns>The deleted key count</returns>
public static long KeysDeleteAll(this IDatabase database, int batchSize = 1000) =>
database.KeysDelete("*", batchSize);


/// <summary>
/// Deletes all keys
/// </summary>
/// <param name="database">The <see cref="IDatabase" /></param>
/// <param name="batchSize">The batch size</param>
/// <returns>The deleted key count</returns>
public static async Task<long> KeysDeleteAllAsync(this IDatabase database, int batchSize = 1000) =>
await database.KeysDeleteAsync("*", batchSize);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class DatabaseExtensionTests
{
[Theory]
[InlineData("pattern", 1000)]
public void KeysDelete_Input_Output_check(string pattern, uint batchSize)
public void KeysDelete_Input_Output_check(string pattern, int batchSize)
{
var database = new Mock<IDatabase>();
database.Setup(database => database.ScriptEvaluate(It.IsAny<string>(),
Expand All @@ -25,7 +25,7 @@ public void KeysDelete_Input_Output_check(string pattern, uint batchSize)

[Theory]
[InlineData("pattern", 1000)]
public async Task KeysDeleteAsync_Input_Output_checkAsync(string pattern, uint batchSize)
public async Task KeysDeleteAsync_Input_Output_checkAsync(string pattern, int batchSize)
{
var database = new Mock<IDatabase>();
database.Setup(database => database.ScriptEvaluateAsync(It.IsAny<string>(),
Expand Down

0 comments on commit 5e8b62a

Please sign in to comment.