I have a rest api calls via I get a config. I cached it by semaphore right now but problem can arrive in case the api is not available.
My code is following:
public async Task<Config> Config(CancellationToken ct = default) {
using (var releaser = await _asyncSemaphore.EnterAsync(ct))
{
if (_serverConfig != null) return _serverConfig;
_serverConfig = await _restApi.GetConfig(ct).ConfigureAwait(false);
return _serverConfig;
}
}
But it can happen, this function can be called from several points within short time period and in a case the _restApi would be down, it would make several calls that timed out synchronously because of the lock. (E.g. 4 calls at the same time, call timeout is 15 seconds so a function can await up to 60 seconds which is not wanted)
I came up with a solution that uses MemoryCache.
public async Task<Config> Config(CancellationToken ct = default)
{
if(_memoryCache.TryGetValue("configTask", out Task<Config> configTask))
{
return await configTask.ConfigureAwait(false);
}
using (var releaser = await _asyncSemaphore.EnterAsync())
{
if (_config != null) return _config;
var task = _memoryCache.Set("configTask", _restApi.Config(ct), TimeSpan.FromSeconds(5));
_serverConfig = await task.ConfigureAwait(false);
return _serverConfig;
}
}
Is anything bad on this approach? Looks to me ok. If the function is called within 5 seconds from the first call, I will always return the first call, otherwise I will wait and do the call again. Not sure, if it's fine to use a MemoryCache for tasks.