Permalink
Browse files

Supporting lazy cache manager resolution

  • Loading branch information...
sebastienros committed Sep 9, 2015
1 parent 2f21344 commit c83f5d88cbdd1e1b4f603d2e5554f012a9f5e16b
Showing with 84 additions and 33 deletions.
  1. +38 −0 src/Orchard.Tests/Caching/CacheTests.cs
  2. +3 −3 src/Orchard.Web/Core/Settings/Metadata/ContentDefinitionManager.cs
  3. +1 −1 src/Orchard.Web/Core/Settings/Services/SiteService.cs
  4. +1 −1 src/Orchard.Web/Modules/Orchard.ContentTypes/Services/StereotypeService.cs
  5. +2 −2 src/Orchard.Web/Modules/Orchard.Layouts/Services/ElementManager.cs
  6. +1 −1 src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/ImageProcessingFileNameProvider.cs
  7. +1 −1 src/Orchard.Web/Modules/Orchard.MediaProcessing/Services/ImageProfileService.cs
  8. +1 −1 src/Orchard.Web/Modules/Orchard.OutputCache/Filters/OutputCacheFilter.cs
  9. +1 −1 src/Orchard.Web/Modules/Orchard.OutputCache/Services/CacheService.cs
  10. +2 −2 src/Orchard.Web/Modules/Orchard.Scripting.Dlr/Services/RubyScriptExpressionEvaluator.cs
  11. +1 −1 src/Orchard.Web/Modules/Orchard.Scripting/ScriptExpressionEvaluator.cs
  12. +1 −1 src/Orchard.Web/Modules/Orchard.Tags/Services/TagCloudService.cs
  13. +1 −1 src/Orchard.Web/Modules/Orchard.Templates/Services/TemplateShapeBindingResolver.cs
  14. +13 −0 src/Orchard/Caching/ICacheManager.cs
  15. +1 −1 src/Orchard/ContentManagement/DefaultContentManager.cs
  16. +1 −1 src/Orchard/ContentManagement/DefaultContentQuery.cs
  17. +1 −1 src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs
  18. +1 −1 src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/PlacementFileParser.cs
  19. +1 −1 src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/ShapeTemplateBindingStrategy.cs
  20. +1 −1 src/Orchard/Environment/Extensions/Compilers/DefaultProjectFileParser.cs
  21. +4 −4 src/Orchard/Environment/Extensions/ExtensionManager.cs
  22. +2 −2 src/Orchard/Environment/Extensions/Folders/ExtensionHarvester.cs
  23. +1 −1 src/Orchard/Environment/IAssemblyNameResolver.cs
  24. +1 −1 src/Orchard/FileSystems/Dependencies/DefaultDependenciesFolder.cs
  25. +1 −1 src/Orchard/FileSystems/Dependencies/DefaultExtensionDependenciesManager.cs
  26. +1 −1 src/Orchard/Localization/Services/DefaultCultureManager.cs
  27. +1 −1 src/Orchard/Localization/Services/DefaultLocalizedStringManager.cs
@@ -1,4 +1,6 @@
using System;
using System.Linq;
using System.Threading;
using Autofac;
using NUnit.Framework;
using Orchard.Caching;
@@ -78,6 +80,42 @@ public class CacheTests {
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; }

@@ -125,7 +125,7 @@ public class ContentDefinitionManager : Component, IContentDefinitionManager {
}

private IDictionary<string, ContentTypeDefinition> AcquireContentTypeDefinitions() {
return _cacheManager.Get("ContentTypeDefinitions", ctx => {
return _cacheManager.Get("ContentTypeDefinitions", true, ctx => {
MonitorContentDefinitionSignal(ctx);

AcquireContentPartDefinitions();
@@ -140,7 +140,7 @@ public class ContentDefinitionManager : Component, IContentDefinitionManager {
}

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 @@ public class ContentDefinitionManager : Component, IContentDefinitionManager {
}

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);
@@ -24,7 +24,7 @@ public class SiteService : ISiteService {
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();
@@ -22,7 +22,7 @@ public class StereotypeService : IStereotypeService {
}

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.
@@ -38,7 +38,7 @@ public class ElementManager : Component, IElementManager {
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 @@ orderby elementDescriptor.DisplayText.Text

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>();
@@ -58,7 +58,7 @@ public class ImageProcessingFileNameProvider : IImageProcessingFileNameProvider
}

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>();

@@ -32,7 +32,7 @@ public class ImageProfileService : IImageProfileService {

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)
@@ -461,7 +461,7 @@ public class OutputCacheFilter : FilterProvider, IActionFilter, IResultFilter {

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>());
}));
@@ -99,7 +99,7 @@ public class CacheService : ICacheService {
}

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();
@@ -14,7 +14,7 @@ public class RubyScriptExpressionEvaluator : IScriptExpressionEvaluator {
}

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 @@ def evaluate(callbacks)
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);
@@ -20,7 +20,7 @@ public class ScriptExpressionEvaluator : IScriptExpressionEvaluator {
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() };
});
@@ -32,7 +32,7 @@ public class TagCloudService : ITagCloudService {

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;
@@ -55,7 +55,7 @@ ITemplateService templateService
}

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
@@ -5,4 +5,17 @@ public interface ICacheManager {
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);
}
}
}
}
@@ -814,7 +814,7 @@ public class DefaultContentManager : IContentManager {
}

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);
@@ -76,7 +76,7 @@ public class DefaultContentQuery : IContentQuery {
}

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);
@@ -37,7 +37,7 @@ Work<IEnumerable<IShapeTableEventHandler>> shapeTableEventHandlersWork
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 => {
@@ -30,7 +30,7 @@ public class PlacementFileParser : IPlacementFileParser {
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);
@@ -77,7 +77,7 @@ public class ShapeTemplateBindingStrategy : IShapeTableProvider {
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>();

@@ -23,7 +23,7 @@ public class DefaultProjectFileParser : IProjectFileParser {
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);
@@ -45,15 +45,15 @@ public class ExtensionManager : IExtensionManager {
}

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)
.ToReadOnlyCollection());
}

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 @@ public class ExtensionManager : IExtensionManager {

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 @@ public class ExtensionManager : IExtensionManager {

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 => {
@@ -56,7 +56,7 @@ public class ExtensionHarvester : IExtensionHarvester {
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 @@ public class ExtensionHarvester : IExtensionHarvester {
}

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));
@@ -37,7 +37,7 @@ public class OrchardFrameworkAssemblyNameResolver : IAssemblyNameResolver {
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)
@@ -34,7 +34,7 @@ public class DefaultDependenciesFolder : IDependenciesFolder {
}

public IEnumerable<DependencyDescriptor> LoadDescriptors() {
return _cacheManager.Get(PersistencePath,
return _cacheManager.Get(PersistencePath, true,
ctx => {
_appDataFolder.CreateDirectory(BasePath);

@@ -65,7 +65,7 @@ public class DefaultExtensionDependenciesManager : IExtensionDependenciesManager
}

public IEnumerable<ActivatedExtensionDescriptor> LoadDescriptors() {
return _cacheManager.Get(PersistencePath, ctx => {
return _cacheManager.Get(PersistencePath, true, ctx => {
_appDataFolder.CreateDirectory(BasePath);

if (!DisableMonitoring) {
@@ -26,7 +26,7 @@ public class DefaultCultureManager : ICultureManager {
}

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();
@@ -90,7 +90,7 @@ public class DefaultLocalizedStringManager : ILocalizedStringManager {
// 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,

0 comments on commit c83f5d8

Please sign in to comment.