Geta / geta-optimizely-genericlinks

An extensive alternative to LinkItemCollection in Optimizely.
Apache License 2.0
4 stars 0 forks source link

LinkDataBackingTypeResolverInterceptor May Throw Exception During Startup #26

Closed rabbtekejos closed 8 months ago

rabbtekejos commented 10 months ago

Geta-Optimmizely-GenericLinks Version: 1.8.1 Optimizely Version: 12.24.0

The exception below is caused by that LinkDataBackingTypeResolverInterceptor.TryResolveType can be called multiple times in parallel. The method does only validate if a type is in the dictionary at the beginning and later calls the Add method on the dictionary if it was resolved, it can result in an exception being thrown if another thread has already resolved and added the type.

I suggest changing to calling TryAdd on the dictionary or using some kind of locking.

Screenshot from just before the exception: You can see in the IEnumerableVisualizer that two types is present in the dictionary and in the Watch window you can see that a duplicate value is about to be added.

image

Logged Exception:

[ERR] Initialize action failed for 'Initialize on class EPiServer.Initialization.Internal.ModelSyncInitialization, EPiServer, Version=12.19.0.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7'
System.AggregateException: One or more errors occurred. (An item with the same key has already been added. Key: XXXXXXX.Features.Linkdata.Models.IconLinkBase)
 ---> System.ArgumentException: An item with the same key has already been added. Key: XXXXXXX.Features.Linkdata.Models.IconLinkBase
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at Geta.Optimizely.GenericLinks.Cms.Registration.LinkDataBackingTypeResolverInterceptor.TryResolveType(Type type, Type baseType)
   at Geta.Optimizely.GenericLinks.Cms.Registration.LinkDataBackingTypeResolverInterceptor.Resolve(Type type)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.PropertyDefinitionSynchronizer.ResolveType(PropertyDefinitionModel model)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.ContentTypeModelRegister.ValidateChangeOfModelType(PropertyDefinitionModel propertyModel, String modelName)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.ContentTypeModelRegister.SetStateForPropertyDefinitionModels(ContentTypeModel model)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.ContentTypeModelRegister.<AnalyzeProperties>b__15_0(ContentTypeModel model)
   at System.Threading.Tasks.Parallel.<>c__DisplayClass33_0`2.<ForEachWorker>b__0(Int32 i)
   at System.Threading.Tasks.Parallel.<>c__DisplayClass19_0`1.<ForWorker>b__1(RangeWorker& currentWorker, Int32 timeout, Boolean& replicationDelegateYieldedBeforeCompletion)
--- End of stack trace from previous location ---
   at System.Threading.Tasks.Parallel.<>c__DisplayClass19_0`1.<ForWorker>b__1(RangeWorker& currentWorker, Int32 timeout, Boolean& replicationDelegateYieldedBeforeCompletion)
   at System.Threading.Tasks.TaskReplicator.Replica.Execute()
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.TaskReplicator.Run[TState](ReplicatableUserAction`1 action, ParallelOptions options, Boolean stopOnFirstFailure)
   at System.Threading.Tasks.Parallel.ForWorker[TLocal](Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Func`4 bodyWithLocal, Func`1 localInit, Action`1 localFinally)
--- End of stack trace from previous location ---
   at System.Threading.Tasks.Parallel.ThrowSingleCancellationExceptionOrOtherException(ICollection exceptions, CancellationToken cancelToken, Exception otherException)
   at System.Threading.Tasks.Parallel.ForWorker[TLocal](Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Func`4 bodyWithLocal, Func`1 localInit, Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IList`1 list, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Action`3 bodyWithStateAndIndex, Func`4 bodyWithStateAndLocal, Func`5 bodyWithEverything, Func`1 localInit, Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable`1 source, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Action`3 bodyWithStateAndIndex, Func`4 bodyWithStateAndLocal, Func`5 bodyWithEverything, Func`1 localInit, Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable`1 source, Action`1 body)
   at EPiServer.DataAbstraction.RuntimeModel.Internal.ContentTypeModelRegister.AnalyzeProperties()
   at EPiServer.DataAbstraction.RuntimeModel.Internal.ContentTypeModelScanner.Sync(Boolean forceCommit)
   at EPiServer.Initialization.Internal.ModelSyncInitialization.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Execute(Action a, String key)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.InitializationEngine.InitializeModules()
svenrog commented 9 months ago

I've submitted a version containing a fix (1.8.2) to the Optimizely NuGet feed. It should be available in a couple of hours.

svenrog commented 8 months ago

Closed due to inactivity.