public class OverviewController : BaseApiController
{
private static readonly ThrottleDictionary throttleDictionary = new();
private readonly TimeSpan throttleTime = TimeSpan.FromSeconds(30);
[HttpGet("api/day/{date}")]
[ApiCallRight("api.get.day")]
public async Task<VmCalendarList> GetDayOverview(DateTime date, bool isChanged = false)
{
var cacheKey = $"dayoverview-{date:yyyy-MM-dd}";
var throttle = throttleDictionary.Get(cacheKey, throttleTime);
var value = (VmCalendarList)throttle.Value;
if (!isChanged && !throttle.CanFire() && value != null) return value;
await throttle.Build(async () =>
{
var absenceInfo = await DomainPorts.OverviewService.DayOverview(date);
var result = VmCalendarList.FromDomain(absenceInfo);
throttle.Value = result;
});
return (VmCalendarList)throttle.Value;
}
}
public class ThrottleDictionary
{
private readonly ConcurrentDictionary<string, Throttle> keyValuePairs = new();
public Throttle Get(string key, TimeSpan throttleTime)
{
if (cleanupThrottle.CanFire()) Cleanup();
return keyValuePairs.GetOrAdd(key, (key) => new Throttle(throttleTime));
}
private static readonly TimeSpan cleanupTime = TimeSpan.FromHours(1);
private readonly Throttle cleanupThrottle = new(cleanupTime);
private void Cleanup()
{
var keysToRemove = keyValuePairs.Where(x => x.Value.LastUsed() < DateTime.Now.Subtract(cleanupTime)).Select(x => x.Key);
foreach(var key in keysToRemove) {
keyValuePairs.Remove(key, out _);
}
}
}
public class Throttle
{
public Throttle(TimeSpan throttleTime)
{
ThrottleTime = throttleTime;
}
private DateTime? Last = null;
private readonly TimeSpan ThrottleTime;
private readonly object Locker = new();
public DateTime? LastUsed() => Last ?? DateTime.Now;
public object Value { get; set; }
public Task BuildTask;
public async Task Build(Func<Task> fn)
{
if (BuildTask != null)
{
await BuildTask;
return;
}
try
{
BuildTask = fn();
await BuildTask;
}
finally
{
BuildTask = null;
}
}
public void Update()
{
Last = DateTime.Now;
}
public bool CanFire()
{
lock(Locker)
{
bool canFire = Last == null || (DateTime.Now - Last).Value > ThrottleTime;
if (canFire) Update();
return canFire;
}
}
}
722200cookie-checkC# Threaded throttle (api call)