Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy InMemory cache replacement. #114

Merged
merged 23 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
904ab9e
Don't show "submitted by" user email public for privacy reasons. (#88)
Zifah Aug 8, 2024
2d2b9ea
Let the variants list link to the variant name. (#89)
Zifah Aug 8, 2024
127767e
Allow privileged users to manage GeoLocations list. (#91)
Zifah Aug 15, 2024
f7475da
Use the CreateDto in all POST/PUT requests to avoid validation proble…
Zifah Aug 15, 2024
b2a4953
Trim string fields during conversion of request DTOs into entities. (…
Zifah Aug 17, 2024
9c9d442
Feature/publish to twitter (#95)
Zifah Aug 23, 2024
709ebb2
Upgrade deployment workflow for the API to.NET 8 on all environments.…
Zifah Aug 23, 2024
219ad16
Maintenance/ivw prep fixes (#98)
Zifah Aug 26, 2024
6ffa9a9
Prevent name posting service from crashing. (#99)
Zifah Aug 30, 2024
51d1136
Merge branch 'PROD' into main
Zifah Aug 30, 2024
4c758d4
Fix social media sharing tags. (#102)
Zifah Sep 2, 2024
ca905a2
Remove unnecessary encoding of base URL in BasePageModel. (#103)
Zifah Sep 2, 2024
4f0732b
Encode name and meaning for social media sharing. (#104)
Zifah Sep 2, 2024
06e57b1
Replace Task.Delay with PeriodicTimer(). (#101)
Zifah Sep 2, 2024
639648d
Merge branch 'PROD' into main
Zifah Sep 2, 2024
d2795de
Don't Encode Text Parts (#106)
Zifah Sep 2, 2024
5cb47b1
Merge branch 'PROD' into main
Zifah Sep 2, 2024
7f40406
Use Hangfire to schedule tweets. (#108)
Zifah Sep 13, 2024
6c6d1b1
Feature/use distributed caching (#110)
Zifah Sep 16, 2024
6867a3e
Reduce tweet attempts to just 3 (#111)
Zifah Sep 16, 2024
20e1acc
Merge branch 'PROD' into main
Zifah Sep 16, 2024
b22a917
Bugfix/remove limit on popular words (#113)
Zifah Sep 17, 2024
3a05acc
Merge branch 'PROD' into main
Zifah Sep 17, 2024
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
2 changes: 0 additions & 2 deletions Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Api.ExceptionHandler;
using Application.Cache;
using Application.Domain;
using Application.Events;
using Application.Migrator;
Expand Down Expand Up @@ -126,7 +125,6 @@
services.AddSingleton<ITwitterService, TwitterService>();
services.AddTwitterClient(configuration);

builder.Services.AddMemoryCache();
builder.Services.SetupHangfire(Guard.Against.NullOrEmpty(configuration.GetRequiredSection("MongoDB:ConnectionString").Value));
builder.Services.SetupRedis(configuration);

Expand Down
2 changes: 1 addition & 1 deletion Core/Cache/IRecentIndexesCache.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Core.Cache
{
public interface IRecentIndexesCache : ICache<string>
public interface IRecentIndexesCache : ISetBasedCache<string>
{
}
}
2 changes: 1 addition & 1 deletion Core/Cache/IRecentSearchesCache.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Core.Cache
{
public interface IRecentSearchesCache : ICache<string>
public interface IRecentSearchesCache : ISetBasedCache<string>
{
Task<IEnumerable<string>> GetMostPopular();
}
Expand Down
2 changes: 1 addition & 1 deletion Core/Cache/ICache.cs → Core/Cache/ISetBasedCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Core.Cache
{
public interface ICache<T>
public interface ISetBasedCache<T>
{
Task<IEnumerable<T>> Get();
Task Stack(T item);
Expand Down
8 changes: 8 additions & 0 deletions Core/Cache/ISimpleCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Core.Cache
{
public interface ISimpleCache
{
Task SetAsync<T>(string key, T value, TimeSpan? expiry = null);
Task<T?> GetAsync<T>(string key);
}
}
2 changes: 2 additions & 0 deletions Infrastructure/Redis/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Ardalis.GuardClauses;
using Core.Cache;
using Infrastructure.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -15,6 +16,7 @@ public static IServiceCollection SetupRedis(this IServiceCollection services, IC
services.Configure<RedisConfig>(configuration.GetRequiredSection(SectionName));
var redisConnectionString = Guard.Against.NullOrEmpty(configuration.GetConnectionString(SectionName));
services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(redisConnectionString));
services.AddSingleton<ISimpleCache, SimpleRedisCache>();
return services;
}
}
Expand Down
15 changes: 9 additions & 6 deletions Infrastructure/Redis/RedisRecentSearchesCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ public class RedisRecentSearchesCache(
{
private const string RecentSearchesKey = "recent_searches";
private const string PopularSearchesKey = "popular_searches";
private static readonly DateTime StartDate;
private const int MaxItemsToReturn = 5;
private const int MaxRecentSearches = 10;
private const int MaxPopularSearches = 1000; // Use a large number to ensure that items have time to get promoted.

static RedisRecentSearchesCache()
{
StartDate = new(2024, 9, 17); // Do not change
}

public async Task<IEnumerable<string>> Get()
{
Expand Down Expand Up @@ -46,8 +51,7 @@ public async Task Stack(string item)

// TODO: Do a periodic caching, like daily where the most popular items from the previous period are brought forward into the next day
var currentScore = (await _cache.SortedSetScoreAsync(PopularSearchesKey, item)) ?? 0;
_ = transaction.SortedSetAddAsync(PopularSearchesKey, item, (int)currentScore + 1 + GetNormalizedTimestamp());
_ = transaction.SortedSetRemoveRangeByRankAsync(PopularSearchesKey, 0, -(MaxPopularSearches + 1));
_ = transaction.SortedSetAddAsync(PopularSearchesKey, item, (int)++currentScore + GetNormalizedTimestamp());

// Execute the transaction
bool committed = await transaction.ExecuteAsync();
Expand All @@ -59,9 +63,8 @@ public async Task Stack(string item)

static double GetNormalizedTimestamp()
{
// This can be improved by addressing the time-cycle reset problem.
long unixTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
return (unixTimestamp % 1000000) / 1000000.0;
TimeSpan timeSinceStartDate = DateTime.Now - StartDate;
return timeSinceStartDate.TotalSeconds / 10_000_000_000; // It will take over 100 years for this value to grow to 1.
}
}
}
32 changes: 32 additions & 0 deletions Infrastructure/Redis/SimpleRedisCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Ardalis.GuardClauses;
using Core.Cache;
using Infrastructure.Configuration;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
namespace Infrastructure.Redis
{
public class SimpleRedisCache(IConnectionMultiplexer connectionMultiplexer, IOptions<RedisConfig> redisConfig) :
RedisCache(connectionMultiplexer, redisConfig), ISimpleCache
{
public async Task<T?> GetAsync<T>(string key)
{
RedisValue theValue = await _cache.StringGetAsync(key);

return theValue.IsNullOrEmpty ? default : ConvertToType<T>(theValue);
}

private static T ConvertToType<T>(RedisValue value)
{
if(typeof(T) == typeof(DateTimeOffset))
{
return (T)(object)DateTimeOffset.Parse(value.ToString());
}
return (T)Convert.ChangeType(value.ToString(), typeof(T));
}

public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
{
await _cache.StringSetAsync(key, Guard.Against.Null(value)!.ToString(), expiry);
}
}
}
12 changes: 6 additions & 6 deletions Infrastructure/Twitter/TwitterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Diagnostics;
using Microsoft.Extensions.Caching.Memory;
using Hangfire;
using Infrastructure.Configuration;
using Core.Cache;

namespace Infrastructure.Twitter
{
public class TwitterService(
ITwitterClientV2 twitterApiClient,
ILogger<TwitterService> logger,
IOptions<TwitterConfig> twitterConfig,
IMemoryCache cache,
ISimpleCache cache,
IBackgroundJobClientV2 backgroundJobClient) : ITwitterService
{
private readonly ITwitterClientV2 _twitterApiClient = twitterApiClient;
private readonly ILogger<TwitterService> _logger = logger;
private readonly TwitterConfig _twitterConfig = twitterConfig.Value;
private readonly IMemoryCache _memoryCache = cache;
private readonly ISimpleCache _simpleCache = cache;
private readonly IBackgroundJobClientV2 _backgroundJobClient = backgroundJobClient;
private static readonly SemaphoreSlim _semaphore;
private const string LastTweetPublishedKey = "LastTweetPublished";
Expand Down Expand Up @@ -48,10 +48,10 @@ private async Task PostTweetAsync(string tweetText, CancellationToken cancellati
await _semaphore.WaitAsync(cancellationToken); // We want to be scheduling only one tweet at a time.
try
{
var foundLastPublished = _memoryCache.TryGetValue(LastTweetPublishedKey, out DateTimeOffset lastTweetPublished);
var lastTweetPublished = await _simpleCache.GetAsync<DateTimeOffset>(LastTweetPublishedKey);
var nextTweetTime = lastTweetPublished.AddSeconds(_twitterConfig.TweetIntervalSeconds);

if (foundLastPublished && nextTweetTime > DateTimeOffset.Now)
if (lastTweetPublished != default && nextTweetTime > DateTimeOffset.Now)
{
_backgroundJobClient.Schedule(() => SendTweetAsync(tweetText), nextTweetTime);
}
Expand All @@ -61,7 +61,7 @@ private async Task PostTweetAsync(string tweetText, CancellationToken cancellati
_backgroundJobClient.Enqueue(() => SendTweetAsync(tweetText));
}

_memoryCache.Set(LastTweetPublishedKey, nextTweetTime);
await _simpleCache.SetAsync(LastTweetPublishedKey, nextTweetTime);
}
finally
{
Expand Down
Loading