Skip to content

Commit aff69db

Browse files
committed
Add an optional prefix for redis cache keys
1 parent 0e9d78f commit aff69db

File tree

3 files changed

+52
-12
lines changed

3 files changed

+52
-12
lines changed

src/HotChocolate/PersistedOperations/src/PersistedOperations.Redis/Extensions/HotChocolateRedisPersistedOperationsRequestExecutorBuilderExtensions.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,23 @@ public static class HotChocolateRedisPersistedOperationsRequestExecutorBuilderEx
2222
/// <param name="queryExpiration">
2323
/// A timeout after which an operation document is removed from the Redis cache.
2424
/// </param>
25+
/// <param name="cacheKeyPrefix">
26+
/// An optional prefix for the cache keys used to store operation documents.
27+
/// </param>
2528
public static IRequestExecutorBuilder AddRedisOperationDocumentStorage(
2629
this IRequestExecutorBuilder builder,
2730
Func<IServiceProvider, IDatabase> databaseFactory,
28-
TimeSpan? queryExpiration = null)
31+
TimeSpan? queryExpiration = null,
32+
string? cacheKeyPrefix = null)
2933
{
3034
ArgumentNullException.ThrowIfNull(builder);
3135
ArgumentNullException.ThrowIfNull(databaseFactory);
3236

3337
return builder.ConfigureSchemaServices(
3438
s => s.AddRedisOperationDocumentStorage(
3539
sp => databaseFactory(sp.GetCombinedServices()),
36-
queryExpiration));
40+
queryExpiration,
41+
cacheKeyPrefix));
3742
}
3843

3944
/// <summary>
@@ -48,18 +53,23 @@ public static IRequestExecutorBuilder AddRedisOperationDocumentStorage(
4853
/// <param name="queryExpiration">
4954
/// A timeout after which an operation document is removed from the Redis cache.
5055
/// </param>
56+
/// <param name="cacheKeyPrefix">
57+
/// An optional prefix for the cache keys used to store operation documents.
58+
/// </param>
5159
public static IRequestExecutorBuilder AddRedisOperationDocumentStorage(
5260
this IRequestExecutorBuilder builder,
5361
Func<IServiceProvider, IConnectionMultiplexer> multiplexerFactory,
54-
TimeSpan? queryExpiration = null)
62+
TimeSpan? queryExpiration = null,
63+
string? cacheKeyPrefix = null)
5564
{
5665
ArgumentNullException.ThrowIfNull(builder);
5766
ArgumentNullException.ThrowIfNull(multiplexerFactory);
5867

5968
return builder.ConfigureSchemaServices(
6069
s => s.AddRedisOperationDocumentStorage(
6170
sp => multiplexerFactory(sp.GetCombinedServices()).GetDatabase(),
62-
queryExpiration));
71+
queryExpiration,
72+
cacheKeyPrefix));
6373
}
6474

6575
/// <summary>
@@ -73,14 +83,19 @@ public static IRequestExecutorBuilder AddRedisOperationDocumentStorage(
7383
/// <param name="queryExpiration">
7484
/// A timeout after which an operation document is removed from the Redis cache.
7585
/// </param>
86+
/// <param name="cacheKeyPrefix">
87+
/// An optional prefix for the cache keys used to store operation documents.
88+
/// </param>
7689
public static IRequestExecutorBuilder AddRedisOperationDocumentStorage(
7790
this IRequestExecutorBuilder builder,
78-
TimeSpan? queryExpiration = null)
91+
TimeSpan? queryExpiration = null,
92+
string? cacheKeyPrefix = null)
7993
{
8094
ArgumentNullException.ThrowIfNull(builder);
8195

8296
return builder.AddRedisOperationDocumentStorage(
8397
sp => sp.GetRequiredService<IConnectionMultiplexer>(),
84-
queryExpiration);
98+
queryExpiration,
99+
cacheKeyPrefix);
85100
}
86101
}

src/HotChocolate/PersistedOperations/src/PersistedOperations.Redis/Extensions/HotChocolateRedisPersistedOperationsServiceCollectionExtensions.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,23 @@ public static class HotChocolateRedisPersistedOperationsServiceCollectionExtensi
2323
/// <param name="queryExpiration">
2424
/// A timeout after which an operation document is removed from the Redis cache.
2525
/// </param>
26+
/// <param name="cacheKeyPrefix">
27+
/// An optional prefix for the cache keys used to store operation documents.
28+
/// This can be useful to avoid key collisions when multiple applications share the same Redis instance.
29+
/// </param>
2630
public static IServiceCollection AddRedisOperationDocumentStorage(
2731
this IServiceCollection services,
2832
Func<IServiceProvider, IDatabase> databaseFactory,
29-
TimeSpan? queryExpiration = null)
33+
TimeSpan? queryExpiration = null,
34+
string? cacheKeyPrefix = null)
3035
{
3136
ArgumentNullException.ThrowIfNull(services);
3237
ArgumentNullException.ThrowIfNull(databaseFactory);
3338

3439
return services
3540
.RemoveService<IOperationDocumentStorage>()
3641
.AddSingleton<IOperationDocumentStorage>(
37-
sp => new RedisOperationDocumentStorage(databaseFactory(sp), queryExpiration));
42+
sp => new RedisOperationDocumentStorage(databaseFactory(sp), queryExpiration, cacheKeyPrefix));
3843
}
3944

4045
private static IServiceCollection RemoveService<TService>(

src/HotChocolate/PersistedOperations/src/PersistedOperations.Redis/RedisOperationDocumentStorage.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class RedisOperationDocumentStorage : IOperationDocumentStorage
1111
{
1212
private readonly IDatabase _database;
1313
private readonly TimeSpan? _expiration;
14+
private readonly string? _cacheKeyPrefix;
1415

1516
/// <summary>
1617
/// Initializes a new instance of the class.
@@ -19,10 +20,14 @@ public class RedisOperationDocumentStorage : IOperationDocumentStorage
1920
/// <param name="expiration">
2021
/// A time span after which an operation document will be removed from the cache.
2122
/// </param>
22-
public RedisOperationDocumentStorage(IDatabase database, TimeSpan? expiration = null)
23+
/// <param name="cacheKeyPrefix">
24+
/// An optional prefix for the cache keys used to store operation documents.
25+
/// </param>
26+
public RedisOperationDocumentStorage(IDatabase database, TimeSpan? expiration = null, string? cacheKeyPrefix = null)
2327
{
2428
_database = database ?? throw new ArgumentNullException(nameof(database));
2529
_expiration = expiration;
30+
_cacheKeyPrefix = cacheKeyPrefix;
2631
}
2732

2833
/// <inheritdoc />
@@ -40,7 +45,7 @@ public RedisOperationDocumentStorage(IDatabase database, TimeSpan? expiration =
4045

4146
private async ValueTask<IOperationDocument?> TryReadInternalAsync(OperationDocumentId documentId)
4247
{
43-
var buffer = (byte[]?)await _database.StringGetAsync(documentId.Value).ConfigureAwait(false);
48+
var buffer = (byte[]?)await _database.StringGetAsync(GetCacheKey(documentId.Value)).ConfigureAwait(false);
4449
return buffer is null ? null : new OperationDocument(Utf8GraphQLParser.Parse(buffer));
4550
}
4651

@@ -65,8 +70,23 @@ private async ValueTask SaveInternalAsync(
6570
IOperationDocument document)
6671
{
6772
var promise = _expiration.HasValue
68-
? _database.StringSetAsync(documentId.Value, document.ToArray(), _expiration.Value)
69-
: _database.StringSetAsync(documentId.Value, document.ToArray());
73+
? _database.StringSetAsync(GetCacheKey(documentId.Value), document.ToArray(), _expiration.Value)
74+
: _database.StringSetAsync(GetCacheKey(documentId.Value), document.ToArray());
7075
await promise.ConfigureAwait(false);
7176
}
77+
78+
private string GetCacheKey(string documentId)
79+
{
80+
if (string.IsNullOrEmpty(documentId))
81+
{
82+
throw new ArgumentNullException(nameof(documentId));
83+
}
84+
85+
if (string.IsNullOrEmpty(_cacheKeyPrefix))
86+
{
87+
return documentId;
88+
}
89+
90+
return $"{_cacheKeyPrefix}:{{{documentId}}}";
91+
}
7292
}

0 commit comments

Comments
 (0)