diff --git a/Modules/Caching/FileSystem/FileSystemCache.cs b/Modules/Caching/FileSystem/FileSystemCache.cs index b8a5ad966..1383b009a 100644 --- a/Modules/Caching/FileSystem/FileSystemCache.cs +++ b/Modules/Caching/FileSystem/FileSystemCache.cs @@ -1,5 +1,6 @@ using System.Text.Json; using System.Text.Json.Serialization; +using AsyncKeyedLock; using GenHTTP.Api.Content.Caching; namespace GenHTTP.Modules.Caching.FileSystem; @@ -13,7 +14,7 @@ public sealed class FileSystemCache : ICache DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; - private readonly SemaphoreSlim _sync = new(1); + private readonly AsyncKeyedLocker _sync = new(); #region Initialization @@ -43,146 +44,113 @@ internal record Index(Dictionary Entries, Dictionary GetEntriesAsync(string key) { - await _sync.WaitAsync(); + using var _ = await _sync.LockAsync(key); + var result = new List(); - try - { - var result = new List(); + var index = await GetIndex(key); - var index = await GetIndex(key); + foreach (var entry in index.Entries) + { + var value = await GetValue(key, entry.Value); - foreach (var entry in index.Entries) + if (value != null) { - var value = await GetValue(key, entry.Value); - - if (value != null) - { - result.Add(value); - } + result.Add(value); } - - return result.ToArray(); - } - finally - { - _sync.Release(); } + + return result.ToArray(); } public async ValueTask GetEntryAsync(string key, string variation) { - await _sync.WaitAsync(); + using var _ = await _sync.LockAsync(key); + var index = await GetIndex(key); - try + if (index.Entries.TryGetValue(variation, out var fileName)) { - var index = await GetIndex(key); - - if (index.Entries.TryGetValue(variation, out var fileName)) - { - return await GetValue(key, fileName); - } - - return default; - } - finally - { - _sync.Release(); + return await GetValue(key, fileName); } + + return default; } public async ValueTask StoreAsync(string key, string variation, T? entry) { - await _sync.WaitAsync(); + using var _ = await _sync.LockAsync(key); + var index = await GetIndex(key); - try - { - var index = await GetIndex(key); + EnsureDirectory(key); - EnsureDirectory(key); + if (index.Entries.TryGetValue(variation, out var fileName)) + { + index.Expiration.Add(fileName, DateTime.UtcNow); - if (index.Entries.TryGetValue(variation, out var fileName)) + if (entry == null) { - index.Expiration.Add(fileName, DateTime.UtcNow); - - if (entry == null) - { - index.Entries.Remove(variation); - } - else - { - fileName = Guid.NewGuid().ToString(); - - index.Entries[variation] = fileName; - - await StoreValue(key, fileName, entry); - } - - await StoreIndex(key, index); + index.Entries.Remove(variation); } - else if (entry != null) + else { - var newFile = Guid.NewGuid().ToString(); - - index.Entries.Add(variation, newFile); + fileName = Guid.NewGuid().ToString(); - await StoreValue(key, newFile, entry); + index.Entries[variation] = fileName; - await StoreIndex(key, index); + await StoreValue(key, fileName, entry); } + await StoreIndex(key, index); } - finally + else if (entry != null) { - _sync.Release(); + var newFile = Guid.NewGuid().ToString(); + + index.Entries.Add(variation, newFile); + + await StoreValue(key, newFile, entry); + + await StoreIndex(key, index); } } public async ValueTask StoreDirectAsync(string key, string variation, Func asyncWriter) { - await _sync.WaitAsync(); + using var _ = await _sync.LockAsync(key); + var index = await GetIndex(key); - try - { - var index = await GetIndex(key); + EnsureDirectory(key); - EnsureDirectory(key); - - if (index.Entries.TryGetValue(variation, out var fileName)) - { - index.Expiration.Add(fileName, DateTime.UtcNow); - - fileName = Guid.NewGuid().ToString(); + if (index.Entries.TryGetValue(variation, out var fileName)) + { + index.Expiration.Add(fileName, DateTime.UtcNow); - index.Entries[variation] = fileName; + fileName = Guid.NewGuid().ToString(); - var file = new FileInfo(Path.Combine(Directory.FullName, key, fileName)); + index.Entries[variation] = fileName; - await using (var streamWriter = new StreamWriter(file.FullName, false)) - { - await asyncWriter(streamWriter.BaseStream); - } + var file = new FileInfo(Path.Combine(Directory.FullName, key, fileName)); - await StoreIndex(key, index); - } - else + await using (var streamWriter = new StreamWriter(file.FullName, false)) { - var newFile = Guid.NewGuid().ToString(); + await asyncWriter(streamWriter.BaseStream); + } - index.Entries.Add(variation, newFile); + await StoreIndex(key, index); + } + else + { + var newFile = Guid.NewGuid().ToString(); - var file = new FileInfo(Path.Combine(Directory.FullName, key, newFile)); + index.Entries.Add(variation, newFile); - await using (var stream = file.OpenWrite()) - { - await asyncWriter(stream); - } + var file = new FileInfo(Path.Combine(Directory.FullName, key, newFile)); - await StoreIndex(key, index); + await using (var stream = file.OpenWrite()) + { + await asyncWriter(stream); } - } - finally - { - _sync.Release(); + + await StoreIndex(key, index); } } diff --git a/Modules/Caching/GenHTTP.Modules.Caching.csproj b/Modules/Caching/GenHTTP.Modules.Caching.csproj index 7062b0146..97c7bd8c2 100644 --- a/Modules/Caching/GenHTTP.Modules.Caching.csproj +++ b/Modules/Caching/GenHTTP.Modules.Caching.csproj @@ -12,6 +12,8 @@ + + diff --git a/Modules/Caching/Memory/MemoryCache.cs b/Modules/Caching/Memory/MemoryCache.cs index ce5a622d6..291f81242 100644 --- a/Modules/Caching/Memory/MemoryCache.cs +++ b/Modules/Caching/Memory/MemoryCache.cs @@ -1,82 +1,65 @@ -using GenHTTP.Api.Content.Caching; +using AsyncKeyedLock; +using GenHTTP.Api.Content.Caching; +using System.Collections.Concurrent; namespace GenHTTP.Modules.Caching.Memory; public sealed class MemoryCache : ICache { - private readonly Dictionary> _cache = new(); - - private readonly SemaphoreSlim _sync = new(1); + private readonly ConcurrentDictionary> _cache = new(); + private readonly AsyncKeyedLocker _sync = new(); #region Functionality - public ValueTask GetEntriesAsync(string key) + public async ValueTask GetEntriesAsync(string key) { - _sync.Wait(); - - try + if (_cache.TryGetValue(key, out var entries)) { - if (_cache.TryGetValue(key, out var entries)) - { - return new ValueTask(entries.Values.ToArray()); - } - - return new ValueTask([]); + return [.. entries.Values]; } - finally + + using var _ = await _sync.LockAsync(key).ConfigureAwait(false); + + if (_cache.TryGetValue(key, out entries)) { - _sync.Release(); + return [.. entries.Values]; } + + return []; } - public ValueTask GetEntryAsync(string key, string variation) + public async ValueTask GetEntryAsync(string key, string variation) { - _sync.Wait(); + using var _ = await _sync.LockAsync(key).ConfigureAwait(false); - try + if (_cache.TryGetValue(key, out var entries)) { - if (_cache.TryGetValue(key, out var entries)) + if (entries.TryGetValue(variation, out var value)) { - if (entries.TryGetValue(variation, out var value)) - { - return new ValueTask(value); - } + return value; } - - return new ValueTask(); - } - finally - { - _sync.Release(); } + + return default; } - public ValueTask StoreAsync(string key, string variation, T? entry) + public async ValueTask StoreAsync(string key, string variation, T? entry) { - _sync.Wait(); + using var _ = await _sync.LockAsync(key).ConfigureAwait(false); - try + if (!_cache.TryGetValue(key, out var value)) { - if (!_cache.TryGetValue(key, out var value)) - { - value = new Dictionary(); - _cache[key] = value; - } - - if (entry != null) - { - value[variation] = entry; - } - else - { - value.Remove(variation); - } + value = []; + _cache[key] = value; + } - return new ValueTask(); + if (entry != null) + { + value[variation] = entry; } - finally + else { - _sync.Release(); + value.Remove(variation); } }