Skip to content

Commit

Permalink
Merge pull request #876 from DFE-Digital/feat/234426/batch-redis-dep-…
Browse files Browse the repository at this point in the history
…management

Feat: batch redis dependency key storage
  • Loading branch information
katie-gardner authored Nov 13, 2024
2 parents a5de47c + f8d7902 commit 222be68
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 31 deletions.
8 changes: 0 additions & 8 deletions src/Dfe.PlanTech.Application/Caching/Interfaces/ICmsCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ namespace Dfe.PlanTech.Application.Caching.Interfaces;
/// </summary>
public interface ICmsCache : IDistributedCache
{
/// <summary>
/// Marks the key as a dependency of all the ContentIds and Slugs within the cached body
/// </summary>
/// <param name="key">Key of the cache item</param>
/// <param name="value">value being stored</param>
/// <returns></returns>
Task RegisterDependenciesAsync<T>(string key, T value);

/// <summary>
/// Iterates through all items in a dependency array and removes them from the cache
/// Then removes the dependency array itself
Expand Down
59 changes: 40 additions & 19 deletions src/Dfe.PlanTech.Infrastructure.Redis/RedisCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public RedisCache(IRedisConnectionManager connectionManager, ILogger<RedisCache>
.OrInner<RedisServerException>()
.OrInner<RedisException>();

_retryPolicyAsync = retryPolicyBuilder.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(2));
_retryPolicyAsync = retryPolicyBuilder.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(50));
}

/// <inheritdoc/>
Expand Down Expand Up @@ -58,7 +58,7 @@ public async Task<string> SetAsync<T>(string key, T value, TimeSpan? expiry = nu
{
_logger.LogInformation("Setting cache item with key: {Key}", key);
var database = await _connectionManager.GetDatabaseAsync(databaseId);
await RegisterDependenciesAsync(key, value);
await RegisterDependenciesAsync(database, key, value);
return await SetAsync(database, key, value, expiry);
}

Expand Down Expand Up @@ -140,7 +140,7 @@ private async Task<string> SetAsync<T>(IDatabase database, string key, T value,
var redisValue = value as string ?? value.Serialise();
_logger.LogInformation("Setting cache item with key: {Key} and value: {Value}", key, redisValue);
await _retryPolicyAsync.ExecuteAsync(() => database.StringSetAsync(key, GZipRedisValueCompressor.Compress(redisValue), expiry));
await RegisterDependenciesAsync(key, value);
await RegisterDependenciesAsync(database, key, value);
return key;
}

Expand Down Expand Up @@ -206,21 +206,18 @@ private static CacheResult<T> CreateCacheResult<T>(RedisValue redisResult)
}

/// <inheritdoc/>
public async Task RegisterDependenciesAsync<T>(string key, T value)
public async Task RegisterDependenciesAsync<T>(IDatabase database, string key, T value)
{
if (value is IEnumerable<ContentComponent> enumerable)
{
foreach (var item in enumerable)
{
await RegisterDependenciesAsync(key, item);
}
}
else if (value is ContentComponent contentComponent)
{
await RegisterContentDependenciesAsync(key, contentComponent);
}
var dependencies = await GetDependenciesAsync(value);
var batch = database.CreateBatch();

var tasks = dependencies.Select(dependency => batch.SetAddAsync(GetDependencyKey(dependency), key)).ToArray();
batch.Execute();

await Task.WhenAll(tasks);
}


/// <inheritdoc/>
public async Task InvalidateCacheAsync(string contentComponentId)
{
Expand All @@ -234,24 +231,48 @@ public async Task InvalidateCacheAsync(string contentComponentId)
await SetRemoveItemsAsync(key, dependencies);
}

/// <inheritdoc/>
private async Task<IEnumerable<string>> GetDependenciesAsync<T>(T value)
{
var result = new List<string>();

if (value is IEnumerable<ContentComponent> enumerable)
{
foreach (var item in enumerable)
{
var nestedDependencies = await GetDependenciesAsync(item);
result.AddRange(nestedDependencies);
}
}
else if (value is ContentComponent contentComponent)
{
var nestedDependencies = await GetContentDependenciesAsync(contentComponent);
result.AddRange(nestedDependencies);
}

return result;
}

/// <summary>
/// Uses reflection to check for any ContentIds within the component and register the parent as a dependency
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
private async Task RegisterContentDependenciesAsync(string key, ContentComponent value)
private async Task<IEnumerable<string>> GetContentDependenciesAsync(ContentComponent value)
{
// add the item itself as a dependency
await SetAddAsync(GetDependencyKey(value.Sys.Id), key);
var results = new List<string> { value.Sys.Id };

var properties = value.GetType().GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(ContentComponent) || typeof(IEnumerable<ContentComponent>).IsAssignableFrom(property.PropertyType))
{
await RegisterDependenciesAsync(key, property.GetValue(value));
var nestedDependencies = await GetDependenciesAsync(property.GetValue(value));
results.AddRange(nestedDependencies);
}
}

return results;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,12 @@ public async Task SetRemoveItemsAsync_Succeeds()
[Fact]
public async Task RegisterDependencyAsync_StoresAllContentIds()
{
await _cache.RegisterDependenciesAsync(key, _question);
await _database.Received(1).SetAddAsync(_cache.GetDependencyKey(_question.Sys.Id), key);
await _database.Received(1).SetAddAsync(_cache.GetDependencyKey(_firstAnswer.Sys.Id), key);
await _database.Received(1).SetAddAsync(_cache.GetDependencyKey(_secondAnswer.Sys.Id), key);
var batch = Substitute.For<IBatch>();
_database.CreateBatch().Returns(batch);
await _cache.RegisterDependenciesAsync(_database, key, _question);
await batch.Received(1).SetAddAsync(_cache.GetDependencyKey(_question.Sys.Id), key);
await batch.Received(1).SetAddAsync(_cache.GetDependencyKey(_firstAnswer.Sys.Id), key);
await batch.Received(1).SetAddAsync(_cache.GetDependencyKey(_secondAnswer.Sys.Id), key);
}

[Fact]
Expand Down

0 comments on commit 222be68

Please sign in to comment.