mirror of
https://github.com/Readarr/Readarr.git
synced 2026-03-05 13:20:32 -05:00
Fixed: Refreshing Plex Server series in high volume systems
(cherry picked from commit 3fb55e9defdb90ab807fcacff249193a4d6114d5)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
@@ -108,7 +108,7 @@ namespace NzbDrone.Common.Test.CacheTests
|
||||
public void should_clear_expired_when_they_expire()
|
||||
{
|
||||
int hitCount = 0;
|
||||
_cachedString = new Cached<string>();
|
||||
_cachedString = new Cached<string>(rollingExpiry: true);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
@@ -123,9 +123,9 @@ namespace NzbDrone.Common.Test.CacheTests
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
Thread.Sleep(2000);
|
||||
|
||||
hitCount.Should().BeInRange(3, 7);
|
||||
hitCount.Should().Be(1);
|
||||
_cachedString.Values.Should().HaveCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace NzbDrone.Common.Cache
|
||||
{
|
||||
ICached<T> GetCache<T>(Type host);
|
||||
ICached<T> GetCache<T>(Type host, string name);
|
||||
ICached<T> GetRollingCache<T>(Type host, string name, TimeSpan defaultLifeTime);
|
||||
ICachedDictionary<T> GetCacheDictionary<T>(Type host, string name, Func<IDictionary<string, T>> fetchFunc = null, TimeSpan? lifeTime = null);
|
||||
void Clear();
|
||||
ICollection<ICached> Caches { get; }
|
||||
@@ -43,6 +44,14 @@ namespace NzbDrone.Common.Cache
|
||||
return (ICached<T>)_cache.Get(host.FullName + "_" + name, () => new Cached<T>());
|
||||
}
|
||||
|
||||
public ICached<T> GetRollingCache<T>(Type host, string name, TimeSpan defaultLifeTime)
|
||||
{
|
||||
Ensure.That(host, () => host).IsNotNull();
|
||||
Ensure.That(name, () => name).IsNotNullOrWhiteSpace();
|
||||
|
||||
return (ICached<T>)_cache.Get(host.FullName + "_" + name, () => new Cached<T>(defaultLifeTime, true));
|
||||
}
|
||||
|
||||
public ICachedDictionary<T> GetCacheDictionary<T>(Type host, string name, Func<IDictionary<string, T>> fetchFunc = null, TimeSpan? lifeTime = null)
|
||||
{
|
||||
Ensure.That(host, () => host).IsNotNull();
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Common.Cache
|
||||
{
|
||||
@@ -29,40 +31,56 @@ namespace NzbDrone.Common.Cache
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<string, CacheItem> _store;
|
||||
private readonly TimeSpan? _defaultLifeTime;
|
||||
private readonly bool _rollingExpiry;
|
||||
|
||||
public Cached()
|
||||
public Cached(TimeSpan? defaultLifeTime = null, bool rollingExpiry = false)
|
||||
{
|
||||
_store = new ConcurrentDictionary<string, CacheItem>();
|
||||
_defaultLifeTime = defaultLifeTime;
|
||||
_rollingExpiry = rollingExpiry;
|
||||
}
|
||||
|
||||
public void Set(string key, T value, TimeSpan? lifetime = null)
|
||||
public void Set(string key, T value, TimeSpan? lifeTime = null)
|
||||
{
|
||||
Ensure.That(key, () => key).IsNotNullOrWhiteSpace();
|
||||
_store[key] = new CacheItem(value, lifetime);
|
||||
|
||||
if (lifetime != null)
|
||||
_store[key] = new CacheItem(value, lifeTime ?? _defaultLifeTime);
|
||||
|
||||
if (lifeTime != null)
|
||||
{
|
||||
System.Threading.Tasks.Task.Delay(lifetime.Value).ContinueWith(t => _store.TryRemove(key, out var temp));
|
||||
ScheduleTryRemove(key, lifeTime.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public T Find(string key)
|
||||
{
|
||||
CacheItem value;
|
||||
_store.TryGetValue(key, out value);
|
||||
|
||||
if (value == null)
|
||||
CacheItem cacheItem;
|
||||
if (!_store.TryGetValue(key, out cacheItem))
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
if (value.IsExpired())
|
||||
if (cacheItem.IsExpired())
|
||||
{
|
||||
_store.TryRemove(key, out value);
|
||||
return default(T);
|
||||
if (TryRemove(key, cacheItem))
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
if (!_store.TryGetValue(key, out cacheItem))
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
return value.Object;
|
||||
if (_rollingExpiry && _defaultLifeTime.HasValue)
|
||||
{
|
||||
_store.TryUpdate(key, new CacheItem(cacheItem.Object, _defaultLifeTime.Value), cacheItem);
|
||||
ScheduleTryRemove(key, _defaultLifeTime.Value);
|
||||
}
|
||||
|
||||
return cacheItem.Object;
|
||||
}
|
||||
|
||||
public void Remove(string key)
|
||||
@@ -77,20 +95,32 @@ namespace NzbDrone.Common.Cache
|
||||
{
|
||||
Ensure.That(key, () => key).IsNotNullOrWhiteSpace();
|
||||
|
||||
CacheItem cacheItem;
|
||||
T value;
|
||||
lifeTime = lifeTime ?? _defaultLifeTime;
|
||||
|
||||
if (!_store.TryGetValue(key, out cacheItem) || cacheItem.IsExpired())
|
||||
CacheItem cacheItem;
|
||||
|
||||
if (_store.TryGetValue(key, out cacheItem) && !cacheItem.IsExpired())
|
||||
{
|
||||
value = function();
|
||||
Set(key, value, lifeTime);
|
||||
if (_rollingExpiry && lifeTime.HasValue)
|
||||
{
|
||||
_store.TryUpdate(key, new CacheItem(cacheItem.Object, lifeTime), cacheItem);
|
||||
ScheduleTryRemove(key, lifeTime.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value = cacheItem.Object;
|
||||
var newCacheItem = new CacheItem(function(), lifeTime);
|
||||
if (cacheItem != null && _store.TryUpdate(key, newCacheItem, cacheItem))
|
||||
{
|
||||
cacheItem = newCacheItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheItem = _store.GetOrAdd(key, newCacheItem);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
return cacheItem.Object;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
@@ -100,9 +130,11 @@ namespace NzbDrone.Common.Cache
|
||||
|
||||
public void ClearExpired()
|
||||
{
|
||||
foreach (var cached in _store.Where(c => c.Value.IsExpired()))
|
||||
var collection = (ICollection<KeyValuePair<string, CacheItem>>)_store;
|
||||
|
||||
foreach (var cached in _store.Where(c => c.Value.IsExpired()).ToList())
|
||||
{
|
||||
Remove(cached.Key);
|
||||
collection.Remove(cached);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,5 +145,23 @@ namespace NzbDrone.Common.Cache
|
||||
return _store.Values.Select(c => c.Object).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryRemove(string key, CacheItem value)
|
||||
{
|
||||
var collection = (ICollection<KeyValuePair<string, CacheItem>>)_store;
|
||||
|
||||
return collection.Remove(new KeyValuePair<string, CacheItem>(key, value));
|
||||
}
|
||||
|
||||
private void ScheduleTryRemove(string key, TimeSpan lifeTime)
|
||||
{
|
||||
Task.Delay(lifeTime).ContinueWith(t =>
|
||||
{
|
||||
if (_store.TryGetValue(key, out var cacheItem) && cacheItem.IsExpired())
|
||||
{
|
||||
_store.TryRemove(key, out _);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Books
|
||||
_authorRepository = authorRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
_authorPathBuilder = authorPathBuilder;
|
||||
_cache = cacheManager.GetCache<List<Author>>(GetType());
|
||||
_cache = cacheManager.GetRollingCache<List<Author>>(GetType(), "authorcache", TimeSpan.FromSeconds(30));
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user