Supporting lazy cache manager resolution

This commit is contained in:
Sebastien Ros
2015-09-08 17:04:45 -07:00
parent 2f2134412e
commit c83f5d88cb
27 changed files with 84 additions and 33 deletions

View File

@@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Threading;
using Autofac;
using NUnit.Framework;
using Orchard.Caching;
@@ -78,6 +80,42 @@ namespace Orchard.Tests.Caching {
Is.Not.SameAs(c2.CacheManager.GetCache<string, string>()));
}
[Test]
public void CacheManagerIsNotBlocking() {
var hits = 0;
string result = "";
Enumerable.Range(0, 5).AsParallel().ForAll(x =>
result = _cacheManager.Get("testItem", ctx => {
// by waiting for 100ms we expect all the calls to Get
// to enter this lambda
Thread.Sleep(100);
hits++;
return "testResult";
})
);
Assert.That(result, Is.EqualTo("testResult"));
Assert.That(hits, Is.GreaterThan(1));
}
[Test]
public void CacheManagerIsBlocking() {
var hits = 0;
string result = "";
Enumerable.Range(0, 5).AsParallel().ForAll(x =>
result = _cacheManager.Get("testItem", true, ctx => {
Thread.Sleep(100);
hits++;
return "testResult";
})
);
Assert.That(result, Is.EqualTo("testResult"));
Assert.That(hits, Is.EqualTo(1));
}
class ComponentOne {
public ICacheManager CacheManager { get; set; }

View File

@@ -125,7 +125,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentTypeDefinition> AcquireContentTypeDefinitions() {
return _cacheManager.Get("ContentTypeDefinitions", ctx => {
return _cacheManager.Get("ContentTypeDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
AcquireContentPartDefinitions();
@@ -140,7 +140,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentPartDefinition> AcquireContentPartDefinitions() {
return _cacheManager.Get("ContentPartDefinitions", ctx => {
return _cacheManager.Get("ContentPartDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
var contentPartDefinitionRecords = _partDefinitionRepository.Table
@@ -153,7 +153,7 @@ namespace Orchard.Core.Settings.Metadata {
}
private IDictionary<string, ContentFieldDefinition> AcquireContentFieldDefinitions() {
return _cacheManager.Get("ContentFieldDefinitions", ctx => {
return _cacheManager.Get("ContentFieldDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);
return _fieldDefinitionRepository.Table.Select(Build).ToDictionary(x => x.Name, y => y);

View File

@@ -24,7 +24,7 @@ namespace Orchard.Core.Settings.Services {
public ILogger Logger { get; set; }
public ISite GetSiteSettings() {
var siteId = _cacheManager.Get("SiteId", ctx => {
var siteId = _cacheManager.Get("SiteId", true, ctx => {
var site = _contentManager.Query("Site")
.List()
.FirstOrDefault();

View File

@@ -22,7 +22,7 @@ namespace Orchard.ContentTypes.Services {
}
public IEnumerable<StereotypeDescription> GetStereotypes() {
return _cacheManager.Get("ContentType.Stereotypes", context => {
return _cacheManager.Get("ContentType.Stereotypes", true, context => {
// TODO: Implement a signal in ContentDefinitionManager that gets raised whenever a type definition is updated.
// For now, we'll just cache the stereotypes for 1 minute.

View File

@@ -38,7 +38,7 @@ namespace Orchard.Layouts.Services {
public IEnumerable<ElementDescriptor> DescribeElements(DescribeElementsContext context) {
var contentType = context.Content != null ? context.Content.ContentItem.ContentType : default(string);
var cacheKey = String.Format("LayoutElementTypes-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam);
return _cacheManager.Get(cacheKey, acquireContext => {
return _cacheManager.Get(cacheKey, true, acquireContext => {
var harvesterContext = new HarvestElementsContext {
Content = context.Content
};
@@ -55,7 +55,7 @@ namespace Orchard.Layouts.Services {
public IEnumerable<CategoryDescriptor> GetCategories(DescribeElementsContext context) {
var contentType = context.Content != null ? context.Content.ContentItem.ContentType : default(string);
return _cacheManager.Get(String.Format("ElementCategories-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam), acquireContext => {
return _cacheManager.Get(String.Format("ElementCategories-{0}-{1}", contentType ?? "AnyType", context.CacheVaryParam), true, acquireContext => {
var elements = DescribeElements(context);
var categoryDictionary = GetCategories();
var categoryDescriptorDictionary = new Dictionary<string, CategoryDescriptor>();

View File

@@ -58,7 +58,7 @@ namespace Orchard.MediaProcessing.Services {
}
private IDictionary<string, string> GetProfileCache(string profile) {
return _cacheManager.Get("MediaProcessing_" + profile, ctx => {
return _cacheManager.Get("MediaProcessing_" + profile, true, ctx => {
ctx.Monitor(_signals.When("MediaProcessing_Saved_" + profile));
var dictionary = new Dictionary<string, string>();

View File

@@ -32,7 +32,7 @@ namespace Orchard.MediaProcessing.Services {
public ImageProfilePart GetImageProfileByName(string name) {
var profileId = _cacheManager.Get("ProfileId_" + name, ctx => {
var profileId = _cacheManager.Get("ProfileId_" + name, true, ctx => {
var profile = _contentManager.Query<ImageProfilePart, ImageProfilePartRecord>()
.Where(x => x.Name == name)
.Slice(0, 1)

View File

@@ -461,7 +461,7 @@ namespace Orchard.OutputCache.Filters {
private CacheSettings CacheSettings {
get {
return _cacheSettings ?? (_cacheSettings = _cacheManager.Get(CacheSettings.CacheKey, context => {
return _cacheSettings ?? (_cacheSettings = _cacheManager.Get(CacheSettings.CacheKey, true, context => {
context.Monitor(_signals.When(CacheSettings.CacheKey));
return new CacheSettings(_workContext.CurrentSite.As<CacheSettingsPart>());
}));

View File

@@ -99,7 +99,7 @@ namespace Orchard.OutputCache.Services {
}
public IEnumerable<CacheRouteConfig> GetRouteConfigs() {
return _cacheManager.Get(RouteConfigsCacheKey,
return _cacheManager.Get(RouteConfigsCacheKey, true,
ctx => {
ctx.Monitor(_signals.When(RouteConfigsCacheKey));
return _repository.Fetch(c => true).Select(c => new CacheRouteConfig { RouteKey = c.RouteKey, Duration = c.Duration, GraceTime = c.GraceTime }).ToReadOnlyCollection();

View File

@@ -14,7 +14,7 @@ namespace Orchard.Scripting.Dlr.Services {
}
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
object execContextType = _cacheManager.Get("---", ctx => (object)_scriptingManager.ExecuteExpression(@"
object execContextType = _cacheManager.Get("---", true, ctx => (object)_scriptingManager.ExecuteExpression(@"
class ExecBlock
def initialize(callbacks)
@callbacks = callbacks
@@ -38,7 +38,7 @@ class ExecContext
end
ExecContext
"));
var ops = _cacheManager.Get("----", ctx => (ObjectOperations)_scriptingManager.ExecuteOperation(x => x));
var ops = _cacheManager.Get("----", true, ctx => (ObjectOperations)_scriptingManager.ExecuteOperation(x => x));
object execContext = _cacheManager.Get(expression, ctx => (object)ops.InvokeMember(execContextType, "alloc", expression));
dynamic result = ops.InvokeMember(execContext, "evaluate", new CallbackApi(this, providers));
return ConvertRubyValue(result);

View File

@@ -20,7 +20,7 @@ namespace Orchard.Scripting {
public Localizer T { get; set; }
public object Evaluate(string expression, IEnumerable<IGlobalMethodProvider> providers) {
var expr = _cacheManager.Get(expression, ctx => {
var expr = _cacheManager.Get(expression, true, ctx => {
var ast = ParseExpression(expression);
return new { Tree = ast, Errors = ast.GetErrors().ToList() };
});

View File

@@ -32,7 +32,7 @@ namespace Orchard.Tags.Services {
public IEnumerable<TagCount> GetPopularTags(int buckets, string slug) {
var cacheKey = "Orchard.Tags.TagCloud." + (slug ?? "") + '.' + buckets;
return _cacheManager.Get(cacheKey,
return _cacheManager.Get(cacheKey, true,
ctx => {
ctx.Monitor(_signals.When(TagCloudTagsChanged));
IEnumerable<TagCount> tagCounts;

View File

@@ -55,7 +55,7 @@ namespace Orchard.Templates.Services {
}
private IDictionary<string, TemplateResult> BuildShapeProcessors() {
return _cacheManager.Get("Template.ShapeProcessors", ctx => {
return _cacheManager.Get("Template.ShapeProcessors", true, ctx => {
ctx.Monitor(_signals.When(DefaultTemplateService.TemplatesSignal));
// select all name of types which contains ShapePart

View File

@@ -5,4 +5,17 @@ namespace Orchard.Caching {
TResult Get<TKey, TResult>(TKey key, Func<AcquireContext<TKey>, TResult> acquire);
ICache<TKey, TResult> GetCache<TKey, TResult>();
}
public static class CacheManagerExtensions {
public static TResult Get<TKey, TResult>(this ICacheManager cacheManager, TKey key, bool lazy, Func<AcquireContext<TKey>, TResult> acquire) {
// Wrap the call in a Lazy initializer to prevent multiple processes from
// executing the same lambda in parallel.
if (lazy) {
return cacheManager.Get<TKey, Lazy<TResult>>(key, k => new Lazy<TResult>(() => acquire(k))).Value;
}
else {
return cacheManager.Get(key, acquire);
}
}
}
}

View File

@@ -814,7 +814,7 @@ namespace Orchard.ContentManagement {
}
private ContentTypeRecord AcquireContentTypeRecord(string contentType) {
var contentTypeId = _cacheManager.Get(contentType + "_Record", ctx => {
var contentTypeId = _cacheManager.Get(contentType + "_Record", true, ctx => {
ctx.Monitor(_signals.When(contentType + "_Record"));
var contentTypeRecord = _contentTypeRepository.Get(x => x.Name == contentType);

View File

@@ -76,7 +76,7 @@ namespace Orchard.ContentManagement {
}
private int GetContentTypeRecordId(string contentType) {
return _cacheManager.Get(contentType + "_Record", ctx => {
return _cacheManager.Get(contentType + "_Record", true, ctx => {
ctx.Monitor(_signals.When(contentType + "_Record"));
var contentTypeRecord = _contentTypeRepository.Get(x => x.Name == contentType);

View File

@@ -37,7 +37,7 @@ namespace Orchard.DisplayManagement.Descriptors {
public ILogger Logger { get; set; }
public ShapeTable GetShapeTable(string themeName) {
return _cacheManager.Get(themeName ?? "", x => {
return _cacheManager.Get(themeName ?? "", true, x => {
Logger.Information("Start building shape table");
var alterationSets = _parallelCacheContext.RunInParallel(_bindingStrategies, bindingStrategy => {

View File

@@ -30,7 +30,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
public bool DisableMonitoring { get; set; }
public PlacementFile Parse(string virtualPath) {
return _cacheManager.Get(virtualPath, context => {
return _cacheManager.Get(virtualPath, true, context => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", virtualPath);

View File

@@ -77,7 +77,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
var pathContexts = harvesterInfos.SelectMany(harvesterInfo => harvesterInfo.subPaths.Select(subPath => {
var basePath = Path.Combine(extensionDescriptor.Location, extensionDescriptor.Id).Replace(Path.DirectorySeparatorChar, '/');
var virtualPath = Path.Combine(basePath, subPath).Replace(Path.DirectorySeparatorChar, '/');
var fileNames = _cacheManager.Get(virtualPath, ctx => {
var fileNames = _cacheManager.Get(virtualPath, true, ctx => {
if (!_virtualPathProvider.DirectoryExists(virtualPath))
return new List<string>();

View File

@@ -23,7 +23,7 @@ namespace Orchard.Environment.Extensions.Compilers {
public bool DisableMonitoring { get; set; }
public ProjectFileDescriptor Parse(string virtualPath) {
return _cacheManager.Get(virtualPath,
return _cacheManager.Get(virtualPath, true,
ctx => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", virtualPath);

View File

@@ -45,7 +45,7 @@ namespace Orchard.Environment.Extensions {
}
public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
return _cacheManager.Get("AvailableExtensions", ctx =>
return _cacheManager.Get("AvailableExtensions", true, ctx =>
_parallelCacheContext
.RunInParallel(_folders, folder => folder.AvailableExtensions().ToList())
.SelectMany(descriptors => descriptors)
@@ -53,7 +53,7 @@ namespace Orchard.Environment.Extensions {
}
public IEnumerable<FeatureDescriptor> AvailableFeatures() {
return _cacheManager.Get("AvailableFeatures", ctx =>
return _cacheManager.Get("AvailableFeatures", true, ctx =>
AvailableExtensions()
.SelectMany(ext => ext.Features)
.OrderByDependenciesAndPriorities(HasDependency, GetPriority)
@@ -93,7 +93,7 @@ namespace Orchard.Environment.Extensions {
var result =
_parallelCacheContext
.RunInParallel(featureDescriptors, descriptor => _cacheManager.Get(descriptor.Id, ctx => LoadFeature(descriptor)))
.RunInParallel(featureDescriptors, descriptor => _cacheManager.Get(descriptor.Id, true, ctx => LoadFeature(descriptor)))
.ToArray();
Logger.Information("Done loading features");
@@ -107,7 +107,7 @@ namespace Orchard.Environment.Extensions {
ExtensionEntry extensionEntry;
try {
extensionEntry = _cacheManager.Get(extensionId, ctx => {
extensionEntry = _cacheManager.Get(extensionId, true, ctx => {
var entry = BuildEntry(extensionDescriptor);
if (entry != null) {
ctx.Monitor(_asyncTokenProvider.GetToken(monitor => {

View File

@@ -56,7 +56,7 @@ namespace Orchard.Environment.Extensions.Folders {
private IEnumerable<ExtensionDescriptor> HarvestExtensions(string path, string extensionType, string manifestName, bool manifestIsOptional) {
string key = string.Format("{0}-{1}-{2}", path, manifestName, extensionType);
return _cacheManager.Get(key, ctx => {
return _cacheManager.Get(key, true, ctx => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", path);
ctx.Monitor(_webSiteFolder.WhenPathChanges(path));
@@ -135,7 +135,7 @@ namespace Orchard.Environment.Extensions.Folders {
}
private ExtensionDescriptor GetExtensionDescriptor(string locationPath, string extensionId, string extensionType, string manifestPath, bool manifestIsOptional) {
return _cacheManager.Get(manifestPath, context => {
return _cacheManager.Get(manifestPath, true, context => {
if (!DisableMonitoring) {
Logger.Debug("Monitoring virtual path \"{0}\"", manifestPath);
context.Monitor(_webSiteFolder.WhenPathChanges(manifestPath));

View File

@@ -37,7 +37,7 @@ namespace Orchard.Environment {
public string Resolve(string shortName) {
// A few common .net framework assemblies are referenced by the Orchard.Framework assembly.
// Look into those to see if we can find the assembly we are looking for.
var orchardFrameworkReferences = _cacheManager.Get(typeof(IAssemblyLoader), ctx =>
var orchardFrameworkReferences = _cacheManager.Get(typeof(IAssemblyLoader), true, ctx =>
ctx.Key.Assembly
.GetReferencedAssemblies()
.GroupBy(n => AssemblyLoaderExtensions.ExtractAssemblyShortName(n.FullName), StringComparer.OrdinalIgnoreCase)

View File

@@ -34,7 +34,7 @@ namespace Orchard.FileSystems.Dependencies {
}
public IEnumerable<DependencyDescriptor> LoadDescriptors() {
return _cacheManager.Get(PersistencePath,
return _cacheManager.Get(PersistencePath, true,
ctx => {
_appDataFolder.CreateDirectory(BasePath);

View File

@@ -65,7 +65,7 @@ namespace Orchard.FileSystems.Dependencies {
}
public IEnumerable<ActivatedExtensionDescriptor> LoadDescriptors() {
return _cacheManager.Get(PersistencePath, ctx => {
return _cacheManager.Get(PersistencePath, true, ctx => {
_appDataFolder.CreateDirectory(BasePath);
if (!DisableMonitoring) {

View File

@@ -26,7 +26,7 @@ namespace Orchard.Localization.Services {
}
public IEnumerable<string> ListCultures() {
return _cacheManager.Get("Cultures", context => {
return _cacheManager.Get("Cultures", true, context => {
context.Monitor(_signals.When("culturesChanged"));
return _cultureRepository.Table.Select(o => o.Culture).ToList();

View File

@@ -90,7 +90,7 @@ namespace Orchard.Localization.Services {
// Cache entry will be invalidated any time the directories hosting
// the .po files are modified.
private CultureDictionary LoadCulture(string culture) {
return _cacheManager.Get(culture, ctx => {
return _cacheManager.Get(culture, true, ctx => {
ctx.Monitor(_signals.When("culturesChanged"));
return new CultureDictionary {
CultureName = culture,