diff --git a/NzbDrone.Core.Test/Framework/CoreTest.cs b/NzbDrone.Core.Test/Framework/CoreTest.cs index 0aed5ed31..558d5402f 100644 --- a/NzbDrone.Core.Test/Framework/CoreTest.cs +++ b/NzbDrone.Core.Test/Framework/CoreTest.cs @@ -1,7 +1,9 @@ using System; using System.IO; +using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers.Core; using NzbDrone.Test.Common; using PetaPoco; @@ -60,5 +62,11 @@ namespace NzbDrone.Core.Test.Framework return new ProgressNotification("Mock notification"); } } + + [TearDown] + public void CoreTestTearDown() + { + ConfigProvider.ClearCache(); + } } } diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 96e742818..4a67ae872 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -113,6 +113,7 @@ + @@ -172,7 +173,7 @@ - + diff --git a/NzbDrone.Core.Test/ProviderTests/ConfigProviderTests/ConfigCachingFixture.cs b/NzbDrone.Core.Test/ProviderTests/ConfigProviderTests/ConfigCachingFixture.cs new file mode 100644 index 000000000..f71184d43 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/ConfigProviderTests/ConfigCachingFixture.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Test.Framework; +using PetaPoco; + +namespace NzbDrone.Core.Test.ProviderTests.ConfigProviderTests +{ + [TestFixture] + public class ConfigCachingFixture : CoreTest + { + [SetUp] + public void Setup() + { + Mocker.GetMock().Setup(c => c.Fetch()) + .Returns(new List { new Config { Key = "Key1", Value = "Value1" } }); + + } + + [Test] + public void getting_value_more_than_once_should_hit_db_once() + { + Mocker.Resolve().GetValue("Key1", null).Should().Be("Value1"); + Mocker.Resolve().GetValue("Key1", null).Should().Be("Value1"); + Mocker.Resolve().GetValue("Key1", null).Should().Be("Value1"); + + Mocker.GetMock().Verify(c => c.Fetch(), Times.Once()); + } + + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/ConfigProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/ConfigProviderTests/ConfigProviderFixture.cs similarity index 92% rename from NzbDrone.Core.Test/ProviderTests/ConfigProviderTest.cs rename to NzbDrone.Core.Test/ProviderTests/ConfigProviderTests/ConfigProviderFixture.cs index 480b7d873..7436a00dc 100644 --- a/NzbDrone.Core.Test/ProviderTests/ConfigProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/ConfigProviderTests/ConfigProviderFixture.cs @@ -6,11 +6,11 @@ using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; using NzbDrone.Core.Test.Framework; -namespace NzbDrone.Core.Test.ProviderTests +namespace NzbDrone.Core.Test.ProviderTests.ConfigProviderTests { [TestFixture] // ReSharper disable InconsistentNaming - public class ConfigProviderTest : CoreTest + public class ConfigProviderFixture : CoreTest { [SetUp] public void SetUp() @@ -134,6 +134,15 @@ namespace NzbDrone.Core.Test.ProviderTests guid.Should().NotBeEmpty(); } + [Test] + public void updating_a_vakye_should_update_its_value() + { + Mocker.Resolve().SabHost = "Test"; + Mocker.Resolve().SabHost.Should().Be("Test"); + + Mocker.Resolve().SabHost = "Test2"; + Mocker.Resolve().SabHost.Should().Be("Test2"); + } [Test] [Description("This test will use reflection to ensure each config property read/writes to a unique key")] diff --git a/NzbDrone.Core/Providers/Core/ConfigProvider.cs b/NzbDrone.Core/Providers/Core/ConfigProvider.cs index 1fb57dd9d..73dac070d 100644 --- a/NzbDrone.Core/Providers/Core/ConfigProvider.cs +++ b/NzbDrone.Core/Providers/Core/ConfigProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Ninject; using NLog; using NzbDrone.Core.Model; @@ -11,7 +12,10 @@ namespace NzbDrone.Core.Providers.Core { public class ConfigProvider { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + private static Dictionary cache = new Dictionary(); + private readonly IDatabase _database; [Inject] @@ -24,7 +28,7 @@ namespace NzbDrone.Core.Providers.Core { } - public IList All() + public IEnumerable All() { return _database.Fetch(); } @@ -409,7 +413,7 @@ namespace NzbDrone.Core.Providers.Core set { SetValue("AutoIgnorePreviouslyDownloadedEpisodes", value); } } - public Guid UGuid + public Guid UGuid { get { return Guid.Parse(GetValue("UGuid", Guid.NewGuid().ToString(), persist: true)); } } @@ -449,12 +453,15 @@ namespace NzbDrone.Core.Providers.Core public virtual string GetValue(string key, object defaultValue, bool persist = false) { - var dbValue = _database.SingleOrDefault("WHERE [Key] =@0", key); + EnsureCache(); + + + string dbValue; - if (dbValue != null && !String.IsNullOrEmpty(dbValue.Value)) - return dbValue.Value; + if (cache.TryGetValue(key, out dbValue) && dbValue != null && !String.IsNullOrEmpty(dbValue)) + return dbValue; - Logger.Trace("Unable to find config key '{0}' defaultValue:'{1}'", key, defaultValue); + logger.Trace("Unable to find config key '{0}' defaultValue:'{1}'", key, defaultValue); if (persist) SetValue(key, defaultValue.ToString()); @@ -479,7 +486,7 @@ namespace NzbDrone.Core.Providers.Core if (value == null) throw new ArgumentNullException("key"); - Logger.Trace("Writing Setting to file. Key:'{0}' Value:'{1}'", key, value); + logger.Trace("Writing Setting to file. Key:'{0}' Value:'{1}'", key, value); var dbValue = _database.SingleOrDefault("WHERE [KEY]=@0", key); @@ -490,12 +497,29 @@ namespace NzbDrone.Core.Providers.Core else { dbValue.Value = value; - using (var tran = _database.GetTransaction()) + _database.Update(dbValue); + } + + ClearCache(); + } + + private void EnsureCache() + { + lock (cache) + { + if (!cache.Any()) { - _database.Update(dbValue); - tran.Complete(); + cache = _database.Fetch().ToDictionary(c => c.Key, c => c.Value); } } } + + public static void ClearCache() + { + lock (cache) + { + cache = new Dictionary(); + } + } } } diff --git a/NzbDrone.Web/Controllers/SystemController.cs b/NzbDrone.Web/Controllers/SystemController.cs index f1217d02e..46fa0f071 100644 --- a/NzbDrone.Web/Controllers/SystemController.cs +++ b/NzbDrone.Web/Controllers/SystemController.cs @@ -48,13 +48,13 @@ namespace NzbDrone.Web.Controllers var jobs = _jobProvider.All().Select(j => new JobModel { - Id = j.Id, - Enable = j.Enable, - TypeName = j.TypeName, - Name = j.Name, - Interval = j.Interval, - LastExecution = j.LastExecution.ToString(), - Success = j.Success + Id = j.Id, + Enable = j.Enable, + TypeName = j.TypeName, + Name = j.Name, + Interval = j.Interval, + LastExecution = j.LastExecution.ToString(), + Success = j.Success }).OrderBy(j => j.Interval); var serializedJobs = new JavaScriptSerializer().Serialize(jobs); @@ -84,9 +84,9 @@ namespace NzbDrone.Web.Controllers return Json(new { - iTotalRecords = config.Count, - iTotalDisplayRecords = config.Count, - aaData = config + iTotalRecords = config.Count(), + iTotalDisplayRecords = config.Count(), + aaData = config }, JsonRequestBehavior.AllowGet); }