Skip to content

Commit

Permalink
Bugfix exception during install, live models collision
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephan committed Feb 22, 2019
1 parent 04e916b commit aa86dfa
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/Umbraco.Core/Composing/ComponentCollectionBuilder.cs
Expand Up @@ -30,7 +30,7 @@ protected override IEnumerable<IComponent> CreateItems(IFactory factory)

protected override IComponent CreateItem(IFactory factory, Type itemType)
{
using (_logger.DebugDuration<Composers>($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
using (_logger.DebugDuration<ComponentCollectionBuilder>($"Creating {itemType.FullName}.", $"Created {itemType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
{
return base.CreateItem(factory, itemType);
}
Expand Down
33 changes: 33 additions & 0 deletions src/Umbraco.Core/PublishedModelFactoryExtensions.cs
@@ -0,0 +1,33 @@
using System;
using Umbraco.Core.Models.PublishedContent;

namespace Umbraco.Core
{
/// <summary>
/// Provides extension methods for <see cref="IPublishedModelFactory"/>.
/// </summary>
public static class PublishedModelFactoryExtensions
{
/// <summary>
/// Executes an action with a safe live factory/
/// </summary>
/// <remarks>
/// <para>If the factory is a live factory, ensures it is refreshed and locked while executing the action.</para>
/// </remarks>
public static void WithSafeLiveFactory(this IPublishedModelFactory factory, Action action)
{
if (factory is ILivePublishedModelFactory liveFactory)
{
lock (liveFactory.SyncRoot)
{
liveFactory.Refresh();
action();
}
}
else
{
action();
}
}
}
}
1 change: 1 addition & 0 deletions src/Umbraco.Core/Umbraco.Core.csproj
Expand Up @@ -212,6 +212,7 @@
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
<Compile Include="PropertyEditors\DateTimeConfiguration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\RenameLabelAndRichTextPropertyEditorAliases.cs" />
<Compile Include="PublishedModelFactoryExtensions.cs" />
<Compile Include="Services\PropertyValidationService.cs" />
<Compile Include="TypeLoaderExtensions.cs" />
<Compile Include="Composing\WeightAttribute.cs" />
Expand Down
4 changes: 2 additions & 2 deletions src/Umbraco.Tests/PublishedContent/NuCacheTests.cs
Expand Up @@ -180,8 +180,8 @@ private void Init()
dataSource,
globalSettings,
new SiteDomainHelper(),
contentTypeServiceBaseFactory,
Mock.Of<IEntityXmlSerializer>());
Mock.Of<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>());

// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();
Expand Down
5 changes: 3 additions & 2 deletions src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs
Expand Up @@ -97,8 +97,9 @@ protected override IPublishedSnapshotService CreatePublishedSnapshotService()
documentRepository, mediaRepository, memberRepository,
DefaultCultureAccessor,
new DatabaseDataSource(),
Factory.GetInstance<IGlobalSettings>(), new SiteDomainHelper(), contentTypeServiceBaseFactory,
Factory.GetInstance<IEntityXmlSerializer>());
Factory.GetInstance<IGlobalSettings>(), new SiteDomainHelper(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>());
}

protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable<IUrlProvider> urlProviders = null)
Expand Down
5 changes: 3 additions & 2 deletions src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs
Expand Up @@ -70,8 +70,9 @@ protected override IPublishedSnapshotService CreatePublishedSnapshotService()
documentRepository, mediaRepository, memberRepository,
DefaultCultureAccessor,
new DatabaseDataSource(),
Factory.GetInstance<IGlobalSettings>(), new SiteDomainHelper(), contentTypeServiceBaseFactory,
Factory.GetInstance<IEntityXmlSerializer>());
Factory.GetInstance<IGlobalSettings>(), new SiteDomainHelper(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>());
}

public class LocalServerMessenger : ServerMessengerBase
Expand Down
17 changes: 3 additions & 14 deletions src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
Expand Down Expand Up @@ -82,20 +83,8 @@ public override void Refresh(JsonPayload[] payloads)
// service of changes, else factories may try to rebuild models while
// we are using the database to load content into caches

// ReSharper disable once SuspiciousTypeConversion.Global
if (_publishedModelFactory is ILivePublishedModelFactory live)
{
lock (live.SyncRoot)
{
live.Refresh();
_publishedSnapshotService.Notify(payloads);
}
}
else
{
// ReSharper disable once InconsistentlySynchronizedField
_publishedSnapshotService.Notify(payloads);
}
_publishedModelFactory.WithSafeLiveFactory(() =>
_publishedSnapshotService.Notify(payloads));

// now we can trigger the event
base.Refresh(payloads);
Expand Down
17 changes: 3 additions & 14 deletions src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs
@@ -1,4 +1,5 @@
using System;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
Expand Down Expand Up @@ -65,20 +66,8 @@ public override void Refresh(JsonPayload[] payloads)
// service of changes, else factories may try to rebuild models while
// we are using the database to load content into caches

// ReSharper disable once SuspiciousTypeConversion.Global
if (_publishedModelFactory is ILivePublishedModelFactory live)
{
lock (live.SyncRoot)
{
live.Refresh();
_publishedSnapshotService.Notify(payloads);
}
}
else
{
// ReSharper disable once InconsistentlySynchronizedField
_publishedSnapshotService.Notify(payloads);
}
_publishedModelFactory.WithSafeLiveFactory(() =>
_publishedSnapshotService.Notify(payloads));

base.Refresh(payloads);
}
Expand Down
Expand Up @@ -44,9 +44,9 @@ class PublishedSnapshotService : PublishedSnapshotServiceBase
private readonly IMemberRepository _memberRepository;
private readonly IGlobalSettings _globalSettings;
private readonly ISiteDomainHelper _siteDomainHelper;
private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider;
private readonly IEntityXmlSerializer _entitySerializer;
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
private readonly IPublishedModelFactory _publishedModelFactory;

// volatile because we read it with no lock
private volatile bool _isReady;
Expand Down Expand Up @@ -88,8 +88,8 @@ class PublishedSnapshotService : PublishedSnapshotServiceBase
IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IScopeProvider scopeProvider,
IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository,
IDefaultCultureAccessor defaultCultureAccessor,
IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider,
IEntityXmlSerializer entitySerializer)
IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper,
IEntityXmlSerializer entitySerializer, IPublishedModelFactory publishedModelFactory)
: base(publishedSnapshotAccessor, variationContextAccessor)
{
//if (Interlocked.Increment(ref _singletonCheck) > 1)
Expand All @@ -107,7 +107,7 @@ class PublishedSnapshotService : PublishedSnapshotServiceBase
_defaultCultureAccessor = defaultCultureAccessor;
_globalSettings = globalSettings;
_siteDomainHelper = siteDomainHelper;
_contentTypeBaseServiceProvider = contentTypeBaseServiceProvider;
_publishedModelFactory = publishedModelFactory;

// we need an Xml serializer here so that the member cache can support XPath,
// for members this is done by navigating the serialized-to-xml member
Expand Down Expand Up @@ -167,7 +167,7 @@ class PublishedSnapshotService : PublishedSnapshotServiceBase

_domainStore = new SnapDictionary<int, Domain>();

LoadCaches();
_publishedModelFactory.WithSafeLiveFactory(LoadCaches);

Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? default;
int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? default;
Expand Down Expand Up @@ -571,6 +571,10 @@ private void LoadDomainsLocked()
// because now we should ALWAYS run with the database server messenger, and then the RefreshAll will
// be processed as soon as we are configured and the messenger processes instructions.

// note: notifications for content type and data type changes should be invoked with the
// pure live model factory, if any, locked and refreshed - see ContentTypeCacheRefresher and
// DataTypeCacheRefresher

public override void Notify(ContentCacheRefresher.JsonPayload[] payloads, out bool draftChanged, out bool publishedChanged)
{
// no cache, trash everything
Expand Down

0 comments on commit aa86dfa

Please sign in to comment.