< Summary

Information
Class: Services.CacheServices.Cache.OzonOrdersCache.ProductCache
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/CacheServices/Cache/OzonOrdersCache/ProductCache.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 68
Coverable lines: 68
Total lines: 171
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 20
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
.ctor(...)100%210%
SetCache(...)100%210%
GetCache(...)0%620%
IsCacheValid()0%620%
Get()0%7280%
Set()0%2040%
Update()100%210%
GetAsync()100%210%
SetAsync()100%210%
UpdateAsync()100%210%
Invalidate()100%210%
InvalidateAsync()100%210%
TryGet()0%2040%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/CacheServices/Cache/OzonOrdersCache/ProductCache.cs

#LineLine coverage
 1using Microsoft.Extensions.Caching.Memory;
 2using OzonDomains.Models;
 3using Services.CacheServices;
 4using Services.DataServices;
 5using System;
 6using System.Collections.Generic;
 7using System.Threading;
 8using System.Threading.Tasks;
 9
 10namespace Services.CacheServices.Cache.OzonOrdersCache
 11{
 12    public class ProductCache : IRedisAppCache<Product>
 13    {
 14        private readonly IMemoryCache _memoryCache;
 15        private readonly ProductsDataServices _productServices;
 16        private const string CacheKey = "Products";
 17        private const string CacheTimestampKey = "Products_Timestamp";
 18        private const int CacheExpirationMinutes = 10;
 019        private static readonly SemaphoreSlim _cacheLock = new SemaphoreSlim(1, 1);
 20
 021        public ProductCache(IMemoryCache memoryCache, ProductsDataServices productsDataServices)
 22        {
 023            _memoryCache = memoryCache;
 024            _productServices = productsDataServices;
 025        }
 26
 27        private void SetCache(string key, object value, TimeSpan expiration)
 28        {
 029            var cacheEntryOptions = new MemoryCacheEntryOptions()
 030                .SetAbsoluteExpiration(expiration)
 031                .SetSlidingExpiration(TimeSpan.FromMinutes(5))
 032                .SetPriority(CacheItemPriority.High)
 033                .RegisterPostEvictionCallback((key, value, reason, state) =>
 034                {
 035                    // Логирование при удалении из кэша
 036                    Console.WriteLine($"Cache entry {key} was evicted: {reason}");
 037                });
 38
 039            _memoryCache.Set(key, value, cacheEntryOptions);
 040        }
 41
 42        private T? GetCache<T>(string key)
 43        {
 044            return _memoryCache.TryGetValue(key, out T? value) ? value : default;
 45        }
 46
 47        private bool IsCacheValid()
 48        {
 049            if (!_memoryCache.TryGetValue(CacheTimestampKey, out DateTime cacheTime))
 050                return false;
 51
 052            return DateTime.UtcNow - cacheTime < TimeSpan.FromMinutes(CacheExpirationMinutes);
 53        }
 54
 55        public async Task<List<Product>> Get()
 56        {
 57            // Проверяем актуальность кэша
 058            if (IsCacheValid())
 59            {
 060                var cachedProducts = GetCache<List<Product>>(CacheKey);
 061                if (cachedProducts != null)
 62                {
 063                    return cachedProducts;
 64                }
 65            }
 66
 67            // Блокируем для предотвращения race condition
 068            await _cacheLock.WaitAsync();
 69            try
 70            {
 71                // Double-check after lock
 072                if (IsCacheValid())
 73                {
 074                    var cachedProducts = GetCache<List<Product>>(CacheKey);
 075                    if (cachedProducts != null)
 76                    {
 077                        return cachedProducts;
 78                    }
 79                }
 80
 81                // Загружаем и кэшируем
 082                return await Set();
 83            }
 84            finally
 85            {
 086                _cacheLock.Release();
 87            }
 088        }
 89
 90        public async Task<List<Product>> Set()
 91        {
 092            await _cacheLock.WaitAsync();
 93            try
 94            {
 095                var products = await _productServices.GetProducts();
 96
 097                if (products == null)
 98                {
 99                    // В случае ошибки, если в кэше есть старые данные, возвращаем их
 0100                    var cachedProducts = GetCache<List<Product>>(CacheKey);
 0101                    if (cachedProducts != null)
 102                    {
 0103                        Console.WriteLine("Failed to load products from service, using cached version");
 0104                        return cachedProducts;
 105                    }
 106
 0107                    throw new InvalidOperationException("Failed to load products and no cached data available");
 108                }
 109
 0110                SetCache(CacheKey, products, TimeSpan.FromMinutes(CacheExpirationMinutes));
 0111                SetCache(CacheTimestampKey, DateTime.UtcNow, TimeSpan.FromMinutes(CacheExpirationMinutes));
 112
 0113                return products;
 114            }
 115            finally
 116            {
 0117                _cacheLock.Release();
 118            }
 0119        }
 120
 121        public async Task Update()
 122        {
 123            // Просто вызываем Set для обновления кэша
 0124            await Set();
 0125        }
 126
 127        public async Task<List<Product>> GetAsync()
 128        {
 0129            return await Get();
 0130        }
 131
 132        public async Task SetAsync()
 133        {
 0134            await Set();
 0135        }
 136
 137        public async Task UpdateAsync()
 138        {
 0139            await Update();
 0140        }
 141
 142        public void Invalidate()
 143        {
 0144            _memoryCache.Remove(CacheKey);
 0145            _memoryCache.Remove(CacheTimestampKey);
 0146        }
 147
 148        public async Task InvalidateAsync()
 149        {
 0150            Invalidate();
 0151            await Task.CompletedTask;
 0152        }
 153
 154        public async Task<bool> TryGet(List<Product> products)
 155        {
 0156            await Task.CompletedTask;
 0157            if (IsCacheValid())
 158            {
 0159                var cachedProducts = GetCache<List<Product>>(CacheKey);
 0160                if (cachedProducts != null)
 161                {
 0162                    products = cachedProducts;
 0163                    return true;
 164                }
 165            }
 166
 0167            products = default!;
 0168            return false;
 0169        }
 170    }
 171}