Ported over #15928 changes for 13.3 RC (#16023)

* Ported over #15928 changes for 13.3 RC

* Use GetOrAdd()

* Lock dictionary initialization

---------

Co-authored-by: Jason Elkin <jasonelkin86@gmail.com>
This commit is contained in:
Kenn Jacobsen
2024-04-16 08:55:40 +02:00
committed by GitHub
parent d765e69713
commit 6379f2fd35

View File

@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using System.Xml.Serialization;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Collections;
@@ -19,7 +20,6 @@ internal class Property : PublishedPropertyBase
private readonly bool _isMember;
private readonly bool _isPreviewing;
private readonly object _locko = new();
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
// the invariant-neutral source and inter values
@@ -33,7 +33,8 @@ internal class Property : PublishedPropertyBase
private object? _interValue;
// the variant source and inter values
private Dictionary<CompositeStringStringKey, SourceInterValue>? _sourceValues;
private readonly object _locko = new();
private ConcurrentDictionary<CompositeStringStringKey, SourceInterValue>? _sourceValues;
private string? _valuesCacheKey;
@@ -66,12 +67,9 @@ internal class Property : PublishedPropertyBase
}
else
{
if (_sourceValues == null)
{
_sourceValues = new Dictionary<CompositeStringStringKey, SourceInterValue>();
}
EnsureSourceValuesInitialized();
_sourceValues[new CompositeStringStringKey(sourceValue.Culture, sourceValue.Segment)]
_sourceValues![new CompositeStringStringKey(sourceValue.Culture, sourceValue.Segment)]
= new SourceInterValue
{
Culture = sourceValue.Culture,
@@ -125,30 +123,27 @@ internal class Property : PublishedPropertyBase
return hasValue.Value;
}
lock (_locko)
value = GetInterValue(culture, segment);
hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter);
if (hasValue.HasValue)
{
value = GetInterValue(culture, segment);
hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter);
if (hasValue.HasValue)
{
return hasValue.Value;
}
CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (!cacheValues.ObjectInitialized)
{
cacheValues.ObjectValue =
PropertyType.ConvertInterToObject(_content, initialCacheLevel, value, _isPreviewing);
cacheValues.ObjectInitialized = true;
}
value = cacheValues.ObjectValue;
return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false;
return hasValue.Value;
}
CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (!cacheValues.ObjectInitialized)
{
cacheValues.ObjectValue =
PropertyType.ConvertInterToObject(_content, initialCacheLevel, value, _isPreviewing);
cacheValues.ObjectInitialized = true;
}
value = cacheValues.ObjectValue;
return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false;
}
public override object? GetSourceValue(string? culture = null, string? segment = null)
@@ -160,19 +155,16 @@ internal class Property : PublishedPropertyBase
return _sourceValue;
}
lock (_locko)
if (_sourceValues == null)
{
if (_sourceValues == null)
{
return null;
}
return _sourceValues.TryGetValue(
new CompositeStringStringKey(culture, segment),
out SourceInterValue? sourceValue)
? sourceValue.SourceValue
: null;
return null;
}
return _sourceValues.TryGetValue(
new CompositeStringStringKey(culture, segment),
out SourceInterValue? sourceValue)
? sourceValue.SourceValue
: null;
}
private CacheValues GetCacheValues(PropertyCacheLevel cacheLevel)
@@ -227,7 +219,6 @@ internal class Property : PublishedPropertyBase
return (CacheValues)cache.Get(ValuesCacheKey, () => new CacheValues())!;
}
// this is always invoked from within a lock, so does not require its own lock
private object? GetInterValue(string? culture, string? segment)
{
if (culture == string.Empty && segment == string.Empty)
@@ -242,21 +233,17 @@ internal class Property : PublishedPropertyBase
return _interValue;
}
if (_sourceValues == null)
{
_sourceValues = new Dictionary<CompositeStringStringKey, SourceInterValue>();
}
EnsureSourceValuesInitialized();
var k = new CompositeStringStringKey(culture, segment);
if (!_sourceValues.TryGetValue(k, out SourceInterValue? vvalue))
{
_sourceValues[k] = vvalue = new SourceInterValue
SourceInterValue vvalue = _sourceValues!.GetOrAdd(k, _ =>
new SourceInterValue
{
Culture = culture,
Segment = segment,
SourceValue = GetSourceValue(culture, segment),
};
}
});
if (vvalue.InterInitialized)
{
@@ -273,23 +260,20 @@ internal class Property : PublishedPropertyBase
_content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment);
object? value;
lock (_locko)
CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (cacheValues.ObjectInitialized)
{
CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (cacheValues.ObjectInitialized)
{
return cacheValues.ObjectValue;
}
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing);
cacheValues.ObjectInitialized = true;
value = cacheValues.ObjectValue;
return cacheValues.ObjectValue;
}
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing);
cacheValues.ObjectInitialized = true;
value = cacheValues.ObjectValue;
return value;
}
@@ -298,22 +282,19 @@ internal class Property : PublishedPropertyBase
{
_content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment);
lock (_locko)
CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (cacheValues.XPathInitialized)
{
CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (cacheValues.XPathInitialized)
{
return cacheValues.XPathValue;
}
cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing);
cacheValues.XPathInitialized = true;
return cacheValues.XPathValue;
}
cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing);
cacheValues.XPathInitialized = true;
return cacheValues.XPathValue;
}
public override object? GetDeliveryApiValue(bool expanding, string? culture = null, string? segment = null)
@@ -321,18 +302,16 @@ internal class Property : PublishedPropertyBase
_content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment);
object? value;
lock (_locko)
{
CacheValue cacheValues = GetCacheValues(expanding ? PropertyType.DeliveryApiCacheLevelForExpansion : PropertyType.DeliveryApiCacheLevel).For(culture, segment);
CacheValue cacheValues = GetCacheValues(expanding ? PropertyType.DeliveryApiCacheLevelForExpansion : PropertyType.DeliveryApiCacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
object? GetDeliveryApiObject() => PropertyType.ConvertInterToDeliveryApiObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing, expanding);
value = expanding
? GetDeliveryApiExpandedObject(cacheValues, GetDeliveryApiObject)
: GetDeliveryApiDefaultObject(cacheValues, GetDeliveryApiObject);
}
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
object? GetDeliveryApiObject() => PropertyType.ConvertInterToDeliveryApiObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing, expanding);
value = expanding
? GetDeliveryApiExpandedObject(cacheValues, GetDeliveryApiObject)
: GetDeliveryApiDefaultObject(cacheValues, GetDeliveryApiObject);
return value;
}
@@ -382,9 +361,9 @@ internal class Property : PublishedPropertyBase
private class CacheValues : CacheValue
{
private Dictionary<CompositeStringStringKey, CacheValue>? _values;
private readonly object _locko = new();
private ConcurrentDictionary<CompositeStringStringKey, CacheValue>? _values;
// this is always invoked from within a lock, so does not require its own lock
public CacheValue For(string? culture, string? segment)
{
if (culture == string.Empty && segment == string.Empty)
@@ -394,14 +373,15 @@ internal class Property : PublishedPropertyBase
if (_values == null)
{
_values = new Dictionary<CompositeStringStringKey, CacheValue>();
lock (_locko)
{
_values ??= InitializeConcurrentDictionary<CompositeStringStringKey, CacheValue>();
}
}
var k = new CompositeStringStringKey(culture, segment);
if (!_values.TryGetValue(k, out CacheValue? value))
{
_values[k] = value = new CacheValue();
}
CacheValue value = _values.GetOrAdd(k, _ => new CacheValue());
return value;
}
@@ -431,5 +411,22 @@ internal class Property : PublishedPropertyBase
public object? InterValue { get; set; }
}
private static ConcurrentDictionary<TKey, TValue> InitializeConcurrentDictionary<TKey, TValue>()
where TKey : notnull
=> new(-1, 5);
private void EnsureSourceValuesInitialized()
{
if (_sourceValues is not null)
{
return;
}
lock (_locko)
{
_sourceValues ??= InitializeConcurrentDictionary<CompositeStringStringKey, SourceInterValue>();
}
}
#endregion
}