From 10c27358265c1785fa064d86d522db38e390c527 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Sat, 20 Nov 2021 00:55:57 -0800 Subject: [PATCH 01/12] Split type-unsafe InternalQueryCache into 4 statically typed caches (#57) * Split type-unsafe InternalQueryCache into three statically typed caches * Remove unused indexSeekCachingRegion * Make BuildEntitySetTypeState static delegate * Rename EntitySetTask.CacheKey -> ItemsQueryCacheKey * Make CacheKey structs readonly --- Orm/Xtensive.Orm/Orm/Entity.cs | 24 ++--- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 80 +++++++------- .../Orm/Internals/Prefetch/EntityContainer.cs | 1 - .../Orm/Internals/Prefetch/EntityGroupTask.cs | 101 ++++++++---------- .../Orm/Internals/Prefetch/EntitySetTask.cs | 97 ++++++++--------- .../Orm/Internals/Prefetch/PrefetchManager.cs | 4 +- .../Providers/SessionHandler.References.cs | 14 +-- .../Orm/Providers/SessionHandler.cs | 2 - Orm/Xtensive.Orm/Orm/StorageNode.cs | 29 +++-- 9 files changed, 168 insertions(+), 184 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Entity.cs b/Orm/Xtensive.Orm/Orm/Entity.cs index 1c301594da..5f292f7f05 100644 --- a/Orm/Xtensive.Orm/Orm/Entity.cs +++ b/Orm/Xtensive.Orm/Orm/Entity.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2020 Xtensive LLC. +// Copyright (C) 2007-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Dmitri Maximov @@ -260,22 +260,22 @@ public void RemoveLater() RemoveLaterInternal(EntityRemoveReason.User); } + private ExecutableProvider ExecutableProviderGenerator((TypeInfo typeInfo, LockMode lockMode, LockBehavior lockBehavior) t) + { + var index = t.typeInfo.Indexes.PrimaryIndex; + var query = index.GetQuery() + .Seek(context => context.GetValue(keyParameter)) + .Lock(() => t.lockMode, () => t.lockBehavior) + .Select(Array.Empty()); + return Session.Compile(query); + } + /// public void Lock(LockMode lockMode, LockBehavior lockBehavior) { var parameterContext = new ParameterContext(); parameterContext.SetValue(keyParameter, Key.Value); - object key = (TypeInfo, lockMode, lockBehavior); - Func generator = tripletObj => { - var triplet = ((TypeInfo, LockMode, LockBehavior)) tripletObj; - var index = triplet.Item1.Indexes.PrimaryIndex; - var query = index.GetQuery() - .Seek(context => context.GetValue(keyParameter)) - .Lock(() => triplet.Item2, () => triplet.Item3) - .Select(Array.Empty()); - return Session.Compile(query); - }; - var source = (ExecutableProvider) Session.StorageNode.InternalQueryCache.GetOrAdd(key, generator); + var source = Session.StorageNode.InternalExecutableProviderCache.GetOrAdd((TypeInfo, lockMode, lockBehavior), ExecutableProviderGenerator); using var recordSetReader = source.GetRecordSetReader(Session, parameterContext); recordSetReader.MoveNext(); } diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index 319c3261e5..77a3e98faa 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -39,10 +39,46 @@ public abstract class EntitySetBase : SessionBound, #if DEBUG private static readonly string storageTestsAssemblyPrefix = "Xtensive.Orm.Tests"; #endif - private static readonly object entitySetCachingRegion = new object(); private static readonly Parameter keyParameter = new Parameter(WellKnown.KeyFieldName); internal static readonly Parameter ownerParameter = new Parameter("Owner"); + private static readonly Func BuildEntitySetTypeState = (object key, EntitySetBase entitySet) => { + var field = ((Pair) key).Second; + var association = field.Associations.Last(); + var query = association.UnderlyingIndex.GetQuery().Seek(context => context.GetValue(keyParameter)); + var seek = entitySet.Session.Compile(query); + var ownerDescriptor = association.OwnerType.Key.TupleDescriptor; + var targetDescriptor = association.TargetType.Key.TupleDescriptor; + + var itemColumnOffsets = association.AuxiliaryType == null + ? association.UnderlyingIndex.ValueColumns + .Where(ci => ci.IsPrimaryKey) + .Select(ci => ci.Field.MappingInfo.Offset) + .ToList() + : Enumerable.Range(0, targetDescriptor.Count).ToList(); + + var keyFieldCount = ownerDescriptor.Count + itemColumnOffsets.Count; + var keyFieldTypes = ownerDescriptor + .Concat(itemColumnOffsets.Select(i => targetDescriptor[i])) + .ToArray(keyFieldCount); + var keyDescriptor = TupleDescriptor.Create(keyFieldTypes); + + var map = Enumerable.Range(0, ownerDescriptor.Count) + .Select(i => new Pair(0, i)) + .Concat(itemColumnOffsets.Select(i => new Pair(1, i))) + .ToArray(keyFieldCount); + var seekTransform = new MapTransform(true, keyDescriptor, map); + + Func itemCtor = null; + if (association.AuxiliaryType != null) { + itemCtor = DelegateHelper.CreateDelegate>(null, + association.AuxiliaryType.UnderlyingType, DelegateHelper.AspectedFactoryMethodName, + Array.Empty()); + } + + return new EntitySetTypeState(seek, seekTransform, itemCtor, entitySet.GetItemCountQueryDelegate(field)); + }; + private readonly Entity owner; private readonly CombineTransform auxilaryTypeKeyTransform; private readonly bool skipOwnerVersionChange; @@ -923,47 +959,7 @@ private static Key GetOwnerKey(Persistent owner) private EntitySetTypeState GetEntitySetTypeState() { EnsureOwnerIsNotRemoved(); - object key = new Pair(entitySetCachingRegion, Field); - Func generator = k => BuildEntitySetTypeState(k, this); - return (EntitySetTypeState) Session.StorageNode.InternalQueryCache.GetOrAdd(key, generator); - } - - private static EntitySetTypeState BuildEntitySetTypeState(object key, EntitySetBase entitySet) - { - var field = ((Pair) key).Second; - var association = field.Associations.Last(); - var query = association.UnderlyingIndex.GetQuery().Seek(context => context.GetValue(keyParameter)); - var seek = entitySet.Session.Compile(query); - var ownerDescriptor = association.OwnerType.Key.TupleDescriptor; - var targetDescriptor = association.TargetType.Key.TupleDescriptor; - - var itemColumnOffsets = association.AuxiliaryType == null - ? association.UnderlyingIndex.ValueColumns - .Where(ci => ci.IsPrimaryKey) - .Select(ci => ci.Field.MappingInfo.Offset) - .ToList() - : Enumerable.Range(0, targetDescriptor.Count).ToList(); - - var keyFieldCount = ownerDescriptor.Count + itemColumnOffsets.Count; - var keyFieldTypes = ownerDescriptor - .Concat(itemColumnOffsets.Select(i => targetDescriptor[i])) - .ToArray(keyFieldCount); - var keyDescriptor = TupleDescriptor.Create(keyFieldTypes); - - var map = Enumerable.Range(0, ownerDescriptor.Count) - .Select(i => new Pair(0, i)) - .Concat(itemColumnOffsets.Select(i => new Pair(1, i))) - .ToArray(keyFieldCount); - var seekTransform = new MapTransform(true, keyDescriptor, map); - - Func itemCtor = null; - if (association.AuxiliaryType != null) { - itemCtor = DelegateHelper.CreateDelegate>(null, - association.AuxiliaryType.UnderlyingType, DelegateHelper.AspectedFactoryMethodName, - ArrayUtils.EmptyArray); - } - - return new EntitySetTypeState(seek, seekTransform, itemCtor, entitySet.GetItemCountQueryDelegate(field)); + return Session.StorageNode.InternalEntitySetCache.GetOrAdd(Field, BuildEntitySetTypeState, this); } private int? GetItemIndex(EntitySetState state, Key key) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityContainer.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityContainer.cs index f55332842c..065ae2c5b9 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityContainer.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityContainer.cs @@ -16,7 +16,6 @@ namespace Xtensive.Orm.Internals.Prefetch [Serializable] internal abstract class EntityContainer { - private static readonly object indexSeekCachingRegion = new object(); private static readonly Parameter seekParameter = new Parameter(WellKnown.KeyFieldName); private SortedDictionary columns; diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs index 16c62c6287..e34ab50556 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs @@ -15,64 +15,73 @@ namespace Xtensive.Orm.Internals.Prefetch { - [Serializable] - internal sealed class EntityGroupTask : IEquatable + internal readonly struct RecordSetCacheKey : IEquatable { - #region Nested classes + public readonly int[] ColumnIndexes; + public readonly TypeInfo Type; + private readonly int cachedHashCode; - private struct CacheKey : IEquatable + public bool Equals(RecordSetCacheKey other) { - public readonly int[] ColumnIndexes; - public readonly TypeInfo Type; - private readonly int cachedHashCode; + if (!Type.Equals(other.Type)) { + return false; + } - public bool Equals(CacheKey other) - { - if (!Type.Equals(other.Type)) { - return false; - } + if (ColumnIndexes.Length != other.ColumnIndexes.Length) { + return false; + } - if (ColumnIndexes.Length != other.ColumnIndexes.Length) { + for (var i = ColumnIndexes.Length - 1; i >= 0; i--) { + if (ColumnIndexes[i] != other.ColumnIndexes[i]) { return false; } - - for (var i = ColumnIndexes.Length - 1; i >= 0; i--) { - if (ColumnIndexes[i] != other.ColumnIndexes[i]) { - return false; - } - } - - return true; } - public override bool Equals(object obj) => - obj is CacheKey other && Equals(other); + return true; + } - public override int GetHashCode() => cachedHashCode; + public override bool Equals(object obj) => + obj is RecordSetCacheKey other && Equals(other); + public override int GetHashCode() => cachedHashCode; - // Constructors - public CacheKey(int[] columnIndexes, TypeInfo type, int cachedHashCode) - { - ColumnIndexes = columnIndexes; - Type = type; - this.cachedHashCode = cachedHashCode; - } - } + // Constructors - #endregion + public RecordSetCacheKey(int[] columnIndexes, TypeInfo type, int cachedHashCode) + { + ColumnIndexes = columnIndexes; + Type = type; + this.cachedHashCode = cachedHashCode; + } + } + [Serializable] + internal sealed class EntityGroupTask : IEquatable + { private const int MaxKeyCountInOneStatement = 40; - private static readonly object recordSetCachingRegion = new object(); private static readonly Parameter> includeParameter = new Parameter>("Keys"); + private static readonly Func CreateRecordSet = cachingKey => { + var selectedColumnIndexes = cachingKey.ColumnIndexes; + var keyColumnsCount = cachingKey.Type.Indexes.PrimaryIndex.KeyColumns.Count; + var keyColumnIndexes = new int[keyColumnsCount]; + foreach (var index in Enumerable.Range(0, keyColumnsCount)) { + keyColumnIndexes[index] = index; + } + + var columnCollectionLength = cachingKey.Type.Indexes.PrimaryIndex.Columns.Count; + return cachingKey.Type.Indexes.PrimaryIndex.GetQuery().Include(IncludeAlgorithm.ComplexCondition, + true, context => context.GetValue(includeParameter), $"includeColumnName-{Guid.NewGuid()}", + keyColumnIndexes).Filter(t => t.GetValue(columnCollectionLength)).Select(selectedColumnIndexes); + }; + private Dictionary keys; private readonly TypeInfo type; private readonly PrefetchManager manager; private List queryTasks; - private readonly CacheKey cacheKey; + private readonly RecordSetCacheKey cacheKey; public CompilableProvider Provider { get; private set; } @@ -132,30 +141,12 @@ private QueryTask CreateQueryTask(List currentKeySet) { var parameterContext = new ParameterContext(); parameterContext.SetValue(includeParameter, currentKeySet); - object key = new Pair(recordSetCachingRegion, cacheKey); - Func generator = CreateRecordSet; var session = manager.Owner.Session; - Provider = (CompilableProvider) session.StorageNode.InternalQueryCache.GetOrAdd(key, generator); + Provider = session.StorageNode.InternalRecordSetCache.GetOrAdd(cacheKey, CreateRecordSet); var executableProvider = session.Compile(Provider); return new QueryTask(executableProvider, session.GetLifetimeToken(), parameterContext); } - private static CompilableProvider CreateRecordSet(object cachingKey) - { - var pair = (Pair) cachingKey; - var selectedColumnIndexes = pair.Second.ColumnIndexes; - var keyColumnsCount = pair.Second.Type.Indexes.PrimaryIndex.KeyColumns.Count; - var keyColumnIndexes = new int[keyColumnsCount]; - foreach (var index in Enumerable.Range(0, keyColumnsCount)) { - keyColumnIndexes[index] = index; - } - - var columnCollectionLength = pair.Second.Type.Indexes.PrimaryIndex.Columns.Count; - return pair.Second.Type.Indexes.PrimaryIndex.GetQuery().Include(IncludeAlgorithm.ComplexCondition, - true, context => context.GetValue(includeParameter), $"includeColumnName-{Guid.NewGuid()}", - keyColumnIndexes).Filter(t => t.GetValue(columnCollectionLength)).Select(selectedColumnIndexes); - } - private void PutLoadedStatesInCache(IEnumerable queryResult, EntityDataReader reader, HashSet foundedKeys) { @@ -221,7 +212,7 @@ public EntityGroupTask(TypeInfo type, int[] columnIndexes, PrefetchManager manag } cachedHashCode ^= type.GetHashCode(); - cacheKey = new CacheKey(columnIndexes, type, cachedHashCode); + cacheKey = new RecordSetCacheKey(columnIndexes, type, cachedHashCode); } } } diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs index 6c27b4531c..2ace149ab4 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2020 Xtensive LLC. +// Copyright (C) 2009-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Alexander Nikolaev @@ -19,53 +19,61 @@ namespace Xtensive.Orm.Internals.Prefetch { - [Serializable] - internal sealed class EntitySetTask : IEquatable + internal readonly struct ItemsQueryCacheKey : IEquatable { - #region Nested classes + public readonly FieldInfo ReferencingField; + public readonly int? ItemCountLimit; + private readonly int cachedHashCode; - private struct CacheKey : IEquatable + public bool Equals(ItemsQueryCacheKey other) { - public readonly FieldInfo ReferencingField; - public readonly int? ItemCountLimit; - private readonly int cachedHashCode; - - public bool Equals(CacheKey other) - { - return (ItemCountLimit == null) == (other.ItemCountLimit == null) - && Equals(other.ReferencingField, ReferencingField); - } + return (ItemCountLimit == null) == (other.ItemCountLimit == null) + && Equals(other.ReferencingField, ReferencingField); + } - public override bool Equals(object obj) => - obj is CacheKey other && Equals(other); + public override bool Equals(object obj) => + obj is ItemsQueryCacheKey other && Equals(other); - public override int GetHashCode() => cachedHashCode; + public override int GetHashCode() => cachedHashCode; - // Constructors + // Constructors - public CacheKey(FieldInfo referencingField, int? itemCountLimit) - { - ReferencingField = referencingField; - ItemCountLimit = itemCountLimit; - unchecked { - cachedHashCode = (ReferencingField.GetHashCode()*397) - ^ (ItemCountLimit.HasValue ? 1 : 0); - } + public ItemsQueryCacheKey(FieldInfo referencingField, int? itemCountLimit) + { + ReferencingField = referencingField; + ItemCountLimit = itemCountLimit; + unchecked { + cachedHashCode = (ReferencingField.GetHashCode() * 397) + ^ (ItemCountLimit.HasValue ? 1 : 0); } } + } - #endregion - - private static readonly object itemsQueryCachingRegion = new object(); + [Serializable] + internal sealed class EntitySetTask : IEquatable + { private static readonly Parameter ownerParameter = new Parameter(WellKnown.KeyFieldName); private static readonly Parameter itemCountLimitParameter = new Parameter("ItemCountLimit"); + private static readonly Func CreateRecordSetLoadingItems = cachingKey => { + var association = cachingKey.ReferencingField.Associations.Last(); + var primaryTargetIndex = association.TargetType.Indexes.PrimaryIndex; + var resultColumns = new List(primaryTargetIndex.Columns.Count); + var result = association.AuxiliaryType == null + ? CreateQueryForDirectAssociation(cachingKey, primaryTargetIndex, resultColumns) + : CreateQueryForAssociationViaAuxType(cachingKey, primaryTargetIndex, resultColumns); + result = result.Select(resultColumns); + if (cachingKey.ItemCountLimit != null) + result = result.Take(context => context.GetValue(itemCountLimitParameter)); + return result; + }; + private readonly Key ownerKey; private readonly bool isOwnerCached; private readonly PrefetchManager manager; private QueryTask itemsQueryTask; private readonly PrefetchFieldDescriptor referencingFieldDescriptor; - private readonly CacheKey cacheKey; + private readonly ItemsQueryCacheKey cacheKey; public FieldInfo ReferencingField {get { return referencingFieldDescriptor.Field; } } @@ -166,11 +174,9 @@ private QueryTask CreateQueryTask() parameterContext.SetValue(itemCountLimitParameter, ItemCountLimit.Value); } - object key = new Pair(itemsQueryCachingRegion, cacheKey); - Func generator = CreateRecordSetLoadingItems; var session = manager.Owner.Session; var scope = new CompiledQueryProcessingScope(null, null, parameterContext, false); - QueryProvider = (CompilableProvider) session.StorageNode.InternalQueryCache.GetOrAdd(key, generator); + QueryProvider = session.StorageNode.InternalItemsQueryCache.GetOrAdd(cacheKey, CreateRecordSetLoadingItems); ExecutableProvider executableProvider; using (scope.Enter()) { executableProvider = session.Compile(QueryProvider); @@ -178,24 +184,9 @@ private QueryTask CreateQueryTask() return new QueryTask(executableProvider, session.GetLifetimeToken(), parameterContext); } - private static CompilableProvider CreateRecordSetLoadingItems(object cachingKey) - { - var pair = (Pair) cachingKey; - var association = pair.Second.ReferencingField.Associations.Last(); - var primaryTargetIndex = association.TargetType.Indexes.PrimaryIndex; - var resultColumns = new List(primaryTargetIndex.Columns.Count); - var result = association.AuxiliaryType == null - ? CreateQueryForDirectAssociation(pair, primaryTargetIndex, resultColumns) - : CreateQueryForAssociationViaAuxType(pair, primaryTargetIndex, resultColumns); - result = result.Select(resultColumns); - if (pair.Second.ItemCountLimit != null) - result = result.Take(context => context.GetValue(itemCountLimitParameter)); - return result; - } - - private static CompilableProvider CreateQueryForAssociationViaAuxType(Pair pair, IndexInfo primaryTargetIndex, List resultColumns) + private static CompilableProvider CreateQueryForAssociationViaAuxType(in ItemsQueryCacheKey cachingKey, IndexInfo primaryTargetIndex, List resultColumns) { - var association = pair.Second.ReferencingField.Associations.Last(); + var association = cachingKey.ReferencingField.Associations.Last(); var associationIndex = association.UnderlyingIndex; var joiningColumns = GetJoiningColumnIndexes(primaryTargetIndex, associationIndex, association.AuxiliaryType != null); @@ -214,10 +205,10 @@ private static CompilableProvider CreateQueryForAssociationViaAuxType(Pair pair, IndexInfo primaryTargetIndex, List resultColumns) + private static CompilableProvider CreateQueryForDirectAssociation(in ItemsQueryCacheKey cachingKey, IndexInfo primaryTargetIndex, List resultColumns) { AddResultColumnIndexes(resultColumns, primaryTargetIndex, 0); - var association = pair.Second.ReferencingField.Associations.Last(); + var association = cachingKey.ReferencingField.Associations.Last(); var field = association.Reversed.OwnerField; var keyColumnTypes = field.Columns.Select(column => column.ValueType).ToList(); return primaryTargetIndex @@ -273,7 +264,7 @@ public EntitySetTask(Key ownerKey, PrefetchFieldDescriptor referencingFieldDescr this.isOwnerCached = isOwnerCached; ItemCountLimit = referencingFieldDescriptor.EntitySetItemCountLimit; this.manager = manager; - cacheKey = new CacheKey(ReferencingField, ItemCountLimit); + cacheKey = new ItemsQueryCacheKey(ReferencingField, ItemCountLimit); } } } diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs index ad8a903ed3..f6c07406ee 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs @@ -22,7 +22,7 @@ internal sealed class PrefetchManager { #region Nested classes - private struct RootContainerCacheKey : IEquatable + private readonly struct RootContainerCacheKey : IEquatable { private readonly int hashCode; @@ -69,7 +69,7 @@ private class RootContainerCacheEntry // Constructors - public RootContainerCacheEntry(RootContainerCacheKey key, SortedDictionary columns, + public RootContainerCacheEntry(in RootContainerCacheKey key, SortedDictionary columns, List columnsToBeLoaded) { Key = key; diff --git a/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs b/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs index 0764c26b1d..e2448f2b9a 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2020 Xtensive LLC. +// Copyright (C) 2009-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Denis Krjuchkov @@ -29,11 +29,7 @@ public virtual IEnumerable GetReferencesTo(Entity target, Associa { if (association.IsPaired) return FindReferences(target, association, true); - object key = new Pair(CachingRegion, association); - Func generator = p => BuildReferencingQuery(((Pair) p).Second); - var pair = (Pair>) Session.StorageNode.InternalQueryCache.GetOrAdd(key, generator); - var recordSet = pair.First; - var parameter = pair.Second; + var (recordSet, parameter) = Session.StorageNode.InternalAssociationCache.GetOrAdd(association, BuildReferencingQuery); var parameterContext = new ParameterContext(); parameterContext.SetValue(parameter, target.Key.Value); ExecutableProvider executableProvider = Session.Compile(recordSet); @@ -94,7 +90,7 @@ private static IEnumerable FindReferences(Entity owner, Associati } } - private static Pair> BuildReferencingQuery(AssociationInfo association) + private static (CompilableProvider, Parameter) BuildReferencingQuery(AssociationInfo association) { var provider = (CompilableProvider)null; var parameter = new Parameter("pTuple"); @@ -178,7 +174,7 @@ private static Pair> BuildReferencingQuery( break; } } - return new Pair>(provider, parameter); + return (provider, parameter); } } -} \ No newline at end of file +} diff --git a/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.cs b/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.cs index b2fb2c7854..e6b4abfe9e 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.cs @@ -19,8 +19,6 @@ namespace Xtensive.Orm.Providers /// public abstract partial class SessionHandler : IDisposable, IAsyncDisposable { - private static readonly object CachingRegion = new object(); - /// /// Gets . /// diff --git a/Orm/Xtensive.Orm/Orm/StorageNode.cs b/Orm/Xtensive.Orm/Orm/StorageNode.cs index 719476000e..d5c399e392 100644 --- a/Orm/Xtensive.Orm/Orm/StorageNode.cs +++ b/Orm/Xtensive.Orm/Orm/StorageNode.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2020 Xtensive LLC. +// Copyright (C) 2014-2021 Xtensive LLC. // This code is distributed under MIT license terms. // See the License.txt file in the project root for more information. // Created by: Denis Krjuchkov @@ -14,6 +14,9 @@ using Xtensive.Orm.Interfaces; using Xtensive.Orm.Model; using Xtensive.Orm.Providers; +using Xtensive.Orm.Rse.Providers; +using Xtensive.Orm.Internals; +using Xtensive.Orm.Internals.Prefetch; namespace Xtensive.Orm { @@ -44,11 +47,25 @@ public sealed class StorageNode : ISessionSource /// public TypeIdRegistry TypeIdRegistry { get; private set; } - internal ConcurrentDictionary InternalQueryCache { get; private set; } + internal ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider> InternalExecutableProviderCache { get; } = + new ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider>(); - internal ConcurrentDictionary KeySequencesCache { get; private set; } + internal ConcurrentDictionary InternalRecordSetCache { get; } = + new ConcurrentDictionary(); - internal ConcurrentDictionary> PersistRequestCache { get; private set; } + internal ConcurrentDictionary InternalItemsQueryCache { get; } = + new ConcurrentDictionary(); + + internal ConcurrentDictionary InternalEntitySetCache { get; } = + new ConcurrentDictionary(); + + internal ConcurrentDictionary)> InternalAssociationCache { get; } = + new ConcurrentDictionary)>(); + + internal ConcurrentDictionary KeySequencesCache { get; } = new ConcurrentDictionary(); + + internal ConcurrentDictionary> PersistRequestCache { get; } + = new ConcurrentDictionary>(); /// public Session OpenSession() @@ -121,10 +138,6 @@ internal StorageNode(Domain domain, NodeConfiguration configuration, ModelMappin Configuration = configuration; Mapping = mapping; TypeIdRegistry = typeIdRegistry; - - KeySequencesCache = new ConcurrentDictionary(); - PersistRequestCache = new ConcurrentDictionary>(); - InternalQueryCache = new ConcurrentDictionary(); } } } From 94c2daab88e131991d7c3354e4313c4824501327 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Wed, 24 Nov 2021 00:21:12 -0800 Subject: [PATCH 02/12] Typecast bugfix of BuildEntitySetTypeState --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index 77a3e98faa..b9f3a8744e 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -42,8 +42,7 @@ public abstract class EntitySetBase : SessionBound, private static readonly Parameter keyParameter = new Parameter(WellKnown.KeyFieldName); internal static readonly Parameter ownerParameter = new Parameter("Owner"); - private static readonly Func BuildEntitySetTypeState = (object key, EntitySetBase entitySet) => { - var field = ((Pair) key).Second; + private static readonly Func BuildEntitySetTypeState = (FieldInfo field, EntitySetBase entitySet) => { var association = field.Associations.Last(); var query = association.UnderlyingIndex.GetQuery().Seek(context => context.GetValue(keyParameter)); var seek = entitySet.Session.Compile(query); From 970aa4a8c25043e474a4c15f1a37fafe06a9bacf Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Fri, 10 Dec 2021 08:58:06 -0800 Subject: [PATCH 03/12] Make ExecutableProviderGenerator static readonly Func --- Orm/Xtensive.Orm/Orm/Entity.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Entity.cs b/Orm/Xtensive.Orm/Orm/Entity.cs index 5f292f7f05..85e1e9b6db 100644 --- a/Orm/Xtensive.Orm/Orm/Entity.cs +++ b/Orm/Xtensive.Orm/Orm/Entity.cs @@ -66,6 +66,12 @@ public abstract class Entity : Persistent, ISerializable, IDeserializationCallback { + private static readonly Func<(TypeInfo typeInfo, LockMode lockMode, LockBehavior lockBehavior), Session, ExecutableProvider> ExecutableProviderGenerator = ((TypeInfo typeInfo, LockMode lockMode, LockBehavior lockBehavior) t, Session session) => + session.Compile(t.typeInfo.Indexes.PrimaryIndex.GetQuery() + .Seek(context => context.GetValue(keyParameter)) + .Lock(() => t.lockMode, () => t.lockBehavior) + .Select(Array.Empty())); + private static readonly Parameter keyParameter = new Parameter(WellKnown.KeyFieldName); private readonly bool changeVersionOnSetAttempt; private EntityState state; @@ -260,22 +266,12 @@ public void RemoveLater() RemoveLaterInternal(EntityRemoveReason.User); } - private ExecutableProvider ExecutableProviderGenerator((TypeInfo typeInfo, LockMode lockMode, LockBehavior lockBehavior) t) - { - var index = t.typeInfo.Indexes.PrimaryIndex; - var query = index.GetQuery() - .Seek(context => context.GetValue(keyParameter)) - .Lock(() => t.lockMode, () => t.lockBehavior) - .Select(Array.Empty()); - return Session.Compile(query); - } - /// public void Lock(LockMode lockMode, LockBehavior lockBehavior) { var parameterContext = new ParameterContext(); parameterContext.SetValue(keyParameter, Key.Value); - var source = Session.StorageNode.InternalExecutableProviderCache.GetOrAdd((TypeInfo, lockMode, lockBehavior), ExecutableProviderGenerator); + var source = Session.StorageNode.InternalExecutableProviderCache.GetOrAdd((TypeInfo, lockMode, lockBehavior), ExecutableProviderGenerator, Session); using var recordSetReader = source.GetRecordSetReader(Session, parameterContext); recordSetReader.MoveNext(); } From 24ddd41855d444af7531fb3b7122932624670a32 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Fri, 10 Dec 2021 09:06:26 -0800 Subject: [PATCH 04/12] privaryIndex var in CreateRecordSet --- Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs index e34ab50556..e19090cccd 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs @@ -65,13 +65,14 @@ internal sealed class EntityGroupTask : IEquatable private static readonly Func CreateRecordSet = cachingKey => { var selectedColumnIndexes = cachingKey.ColumnIndexes; - var keyColumnsCount = cachingKey.Type.Indexes.PrimaryIndex.KeyColumns.Count; + var privaryIndex = cachingKey.Type.Indexes.PrimaryIndex; + var keyColumnsCount = privaryIndex.KeyColumns.Count; var keyColumnIndexes = new int[keyColumnsCount]; foreach (var index in Enumerable.Range(0, keyColumnsCount)) { keyColumnIndexes[index] = index; } - var columnCollectionLength = cachingKey.Type.Indexes.PrimaryIndex.Columns.Count; + var columnCollectionLength = privaryIndex.Columns.Count; return cachingKey.Type.Indexes.PrimaryIndex.GetQuery().Include(IncludeAlgorithm.ComplexCondition, true, context => context.GetValue(includeParameter), $"includeColumnName-{Guid.NewGuid()}", keyColumnIndexes).Filter(t => t.GetValue(columnCollectionLength)).Select(selectedColumnIndexes); From 9c5f79c41b1547b236677689d5662be483226862 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Fri, 10 Dec 2021 09:07:56 -0800 Subject: [PATCH 05/12] privaryIndex var in CreateRecordSet --- Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs index e19090cccd..543de99328 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs @@ -73,7 +73,7 @@ internal sealed class EntityGroupTask : IEquatable } var columnCollectionLength = privaryIndex.Columns.Count; - return cachingKey.Type.Indexes.PrimaryIndex.GetQuery().Include(IncludeAlgorithm.ComplexCondition, + return privaryIndex.GetQuery().Include(IncludeAlgorithm.ComplexCondition, true, context => context.GetValue(includeParameter), $"includeColumnName-{Guid.NewGuid()}", keyColumnIndexes).Filter(t => t.GetValue(columnCollectionLength)).Select(selectedColumnIndexes); }; From 9b66a456e76fbabe3cbebd3eaaf76fd40d509c9c Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Fri, 10 Dec 2021 09:12:49 -0800 Subject: [PATCH 06/12] Refactor BuildEntitySetTypeState --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 75 ++++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index b9f3a8744e..da58554e9f 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -42,41 +42,7 @@ public abstract class EntitySetBase : SessionBound, private static readonly Parameter keyParameter = new Parameter(WellKnown.KeyFieldName); internal static readonly Parameter ownerParameter = new Parameter("Owner"); - private static readonly Func BuildEntitySetTypeState = (FieldInfo field, EntitySetBase entitySet) => { - var association = field.Associations.Last(); - var query = association.UnderlyingIndex.GetQuery().Seek(context => context.GetValue(keyParameter)); - var seek = entitySet.Session.Compile(query); - var ownerDescriptor = association.OwnerType.Key.TupleDescriptor; - var targetDescriptor = association.TargetType.Key.TupleDescriptor; - - var itemColumnOffsets = association.AuxiliaryType == null - ? association.UnderlyingIndex.ValueColumns - .Where(ci => ci.IsPrimaryKey) - .Select(ci => ci.Field.MappingInfo.Offset) - .ToList() - : Enumerable.Range(0, targetDescriptor.Count).ToList(); - - var keyFieldCount = ownerDescriptor.Count + itemColumnOffsets.Count; - var keyFieldTypes = ownerDescriptor - .Concat(itemColumnOffsets.Select(i => targetDescriptor[i])) - .ToArray(keyFieldCount); - var keyDescriptor = TupleDescriptor.Create(keyFieldTypes); - - var map = Enumerable.Range(0, ownerDescriptor.Count) - .Select(i => new Pair(0, i)) - .Concat(itemColumnOffsets.Select(i => new Pair(1, i))) - .ToArray(keyFieldCount); - var seekTransform = new MapTransform(true, keyDescriptor, map); - - Func itemCtor = null; - if (association.AuxiliaryType != null) { - itemCtor = DelegateHelper.CreateDelegate>(null, - association.AuxiliaryType.UnderlyingType, DelegateHelper.AspectedFactoryMethodName, - Array.Empty()); - } - - return new EntitySetTypeState(seek, seekTransform, itemCtor, entitySet.GetItemCountQueryDelegate(field)); - }; + private static readonly Func EntitySetTypeStateFactory = BuildEntitySetTypeState; private readonly Entity owner; private readonly CombineTransform auxilaryTypeKeyTransform; @@ -958,7 +924,44 @@ private static Key GetOwnerKey(Persistent owner) private EntitySetTypeState GetEntitySetTypeState() { EnsureOwnerIsNotRemoved(); - return Session.StorageNode.InternalEntitySetCache.GetOrAdd(Field, BuildEntitySetTypeState, this); + return Session.StorageNode.InternalEntitySetCache.GetOrAdd(Field, EntitySetTypeStateFactory, this); + } + + private static EntitySetTypeState BuildEntitySetTypeState(FieldInfo field, EntitySetBase entitySet) + { + var association = field.Associations.Last(); + var query = association.UnderlyingIndex.GetQuery().Seek(context => context.GetValue(keyParameter)); + var seek = entitySet.Session.Compile(query); + var ownerDescriptor = association.OwnerType.Key.TupleDescriptor; + var targetDescriptor = association.TargetType.Key.TupleDescriptor; + + var itemColumnOffsets = association.AuxiliaryType == null + ? association.UnderlyingIndex.ValueColumns + .Where(ci => ci.IsPrimaryKey) + .Select(ci => ci.Field.MappingInfo.Offset) + .ToList() + : Enumerable.Range(0, targetDescriptor.Count).ToList(); + + var keyFieldCount = ownerDescriptor.Count + itemColumnOffsets.Count; + var keyFieldTypes = ownerDescriptor + .Concat(itemColumnOffsets.Select(i => targetDescriptor[i])) + .ToArray(keyFieldCount); + var keyDescriptor = TupleDescriptor.Create(keyFieldTypes); + + var map = Enumerable.Range(0, ownerDescriptor.Count) + .Select(i => new Pair(0, i)) + .Concat(itemColumnOffsets.Select(i => new Pair(1, i))) + .ToArray(keyFieldCount); + var seekTransform = new MapTransform(true, keyDescriptor, map); + + Func itemCtor = null; + if (association.AuxiliaryType != null) { + itemCtor = DelegateHelper.CreateDelegate>(null, + association.AuxiliaryType.UnderlyingType, DelegateHelper.AspectedFactoryMethodName, + Array.Empty()); + } + + return new EntitySetTypeState(seek, seekTransform, itemCtor, entitySet.GetItemCountQueryDelegate(field)); } private int? GetItemIndex(EntitySetState state, Key key) From 290e27e4418243618794d8a4eb3a2cc62dedad0c Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Fri, 10 Dec 2021 09:19:15 -0800 Subject: [PATCH 07/12] Rename internal cache names, add comments --- Orm/Xtensive.Orm/Orm/Entity.cs | 2 +- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 2 +- .../Orm/Internals/Prefetch/EntityGroupTask.cs | 2 +- .../Orm/Internals/Prefetch/EntitySetTask.cs | 2 +- .../Providers/SessionHandler.References.cs | 2 +- Orm/Xtensive.Orm/Orm/StorageNode.cs | 25 +++++++++++++++---- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Entity.cs b/Orm/Xtensive.Orm/Orm/Entity.cs index 85e1e9b6db..5d5aafc5f8 100644 --- a/Orm/Xtensive.Orm/Orm/Entity.cs +++ b/Orm/Xtensive.Orm/Orm/Entity.cs @@ -271,7 +271,7 @@ public void Lock(LockMode lockMode, LockBehavior lockBehavior) { var parameterContext = new ParameterContext(); parameterContext.SetValue(keyParameter, Key.Value); - var source = Session.StorageNode.InternalExecutableProviderCache.GetOrAdd((TypeInfo, lockMode, lockBehavior), ExecutableProviderGenerator, Session); + var source = Session.StorageNode.EntityLockProviderCache .GetOrAdd((TypeInfo, lockMode, lockBehavior), ExecutableProviderGenerator, Session); using var recordSetReader = source.GetRecordSetReader(Session, parameterContext); recordSetReader.MoveNext(); } diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index da58554e9f..9469c8d219 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -924,7 +924,7 @@ private static Key GetOwnerKey(Persistent owner) private EntitySetTypeState GetEntitySetTypeState() { EnsureOwnerIsNotRemoved(); - return Session.StorageNode.InternalEntitySetCache.GetOrAdd(Field, EntitySetTypeStateFactory, this); + return Session.StorageNode.EntitySetTypeStateCache .GetOrAdd(Field, EntitySetTypeStateFactory, this); } private static EntitySetTypeState BuildEntitySetTypeState(FieldInfo field, EntitySetBase entitySet) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs index 543de99328..96870e7282 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs @@ -143,7 +143,7 @@ private QueryTask CreateQueryTask(List currentKeySet) var parameterContext = new ParameterContext(); parameterContext.SetValue(includeParameter, currentKeySet); var session = manager.Owner.Session; - Provider = session.StorageNode.InternalRecordSetCache.GetOrAdd(cacheKey, CreateRecordSet); + Provider = session.StorageNode.EntityFetchQueryCache.GetOrAdd(cacheKey, CreateRecordSet); var executableProvider = session.Compile(Provider); return new QueryTask(executableProvider, session.GetLifetimeToken(), parameterContext); } diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs index 2ace149ab4..21abc20593 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs @@ -176,7 +176,7 @@ private QueryTask CreateQueryTask() var session = manager.Owner.Session; var scope = new CompiledQueryProcessingScope(null, null, parameterContext, false); - QueryProvider = session.StorageNode.InternalItemsQueryCache.GetOrAdd(cacheKey, CreateRecordSetLoadingItems); + QueryProvider = session.StorageNode.EntitySetFetchQueryCache .GetOrAdd(cacheKey, CreateRecordSetLoadingItems); ExecutableProvider executableProvider; using (scope.Enter()) { executableProvider = session.Compile(QueryProvider); diff --git a/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs b/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs index e2448f2b9a..29049f402a 100644 --- a/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs +++ b/Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs @@ -29,7 +29,7 @@ public virtual IEnumerable GetReferencesTo(Entity target, Associa { if (association.IsPaired) return FindReferences(target, association, true); - var (recordSet, parameter) = Session.StorageNode.InternalAssociationCache.GetOrAdd(association, BuildReferencingQuery); + var (recordSet, parameter) = Session.StorageNode.RefsToEntityQueryCache .GetOrAdd(association, BuildReferencingQuery); var parameterContext = new ParameterContext(); parameterContext.SetValue(parameter, target.Key.Value); ExecutableProvider executableProvider = Session.Compile(recordSet); diff --git a/Orm/Xtensive.Orm/Orm/StorageNode.cs b/Orm/Xtensive.Orm/Orm/StorageNode.cs index d5c399e392..412a8c6d9e 100644 --- a/Orm/Xtensive.Orm/Orm/StorageNode.cs +++ b/Orm/Xtensive.Orm/Orm/StorageNode.cs @@ -47,19 +47,34 @@ public sealed class StorageNode : ISessionSource /// public TypeIdRegistry TypeIdRegistry { get; private set; } - internal ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider> InternalExecutableProviderCache { get; } = + /// + /// Caches providers that lock certain type of entity with certain and . + /// + internal ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider> EntityLockProviderCache { get; } = new ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider>(); - internal ConcurrentDictionary InternalRecordSetCache { get; } = + /// + /// Caches uncompiled queries used by to fetch certain entities. + /// + internal ConcurrentDictionary EntityFetchQueryCache { get; } = new ConcurrentDictionary(); - internal ConcurrentDictionary InternalItemsQueryCache { get; } = + /// + /// Caches uncompiled queries used by to fetch content. + /// + internal ConcurrentDictionary EntitySetFetchQueryCache { get; } = new ConcurrentDictionary(); - internal ConcurrentDictionary InternalEntitySetCache { get; } = + /// + /// Caches certain info about EntitySet fields, e.g. queries to fetch current count or items. + /// + internal ConcurrentDictionary EntitySetTypeStateCache { get; } = new ConcurrentDictionary(); - internal ConcurrentDictionary)> InternalAssociationCache { get; } = + /// + /// Caches queries that get references to entities for certain association. + /// + internal ConcurrentDictionary)> RefsToEntityQueryCache { get; } = new ConcurrentDictionary)>(); internal ConcurrentDictionary KeySequencesCache { get; } = new ConcurrentDictionary(); From d186c2c8766cd8da1fa08babe7999503e16d96e8 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Fri, 10 Dec 2021 09:24:07 -0800 Subject: [PATCH 08/12] Move internal caches inititalization into constructor --- Orm/Xtensive.Orm/Orm/StorageNode.cs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/StorageNode.cs b/Orm/Xtensive.Orm/Orm/StorageNode.cs index 412a8c6d9e..7ee02aa967 100644 --- a/Orm/Xtensive.Orm/Orm/StorageNode.cs +++ b/Orm/Xtensive.Orm/Orm/StorageNode.cs @@ -50,37 +50,29 @@ public sealed class StorageNode : ISessionSource /// /// Caches providers that lock certain type of entity with certain and . /// - internal ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider> EntityLockProviderCache { get; } = - new ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider>(); + internal ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider> EntityLockProviderCache { get; } /// /// Caches uncompiled queries used by to fetch certain entities. /// - internal ConcurrentDictionary EntityFetchQueryCache { get; } = - new ConcurrentDictionary(); + internal ConcurrentDictionary EntityFetchQueryCache { get; } /// /// Caches uncompiled queries used by to fetch content. /// - internal ConcurrentDictionary EntitySetFetchQueryCache { get; } = - new ConcurrentDictionary(); + internal ConcurrentDictionary EntitySetFetchQueryCache { get; } /// /// Caches certain info about EntitySet fields, e.g. queries to fetch current count or items. /// - internal ConcurrentDictionary EntitySetTypeStateCache { get; } = - new ConcurrentDictionary(); + internal ConcurrentDictionary EntitySetTypeStateCache { get; } /// /// Caches queries that get references to entities for certain association. /// - internal ConcurrentDictionary)> RefsToEntityQueryCache { get; } = - new ConcurrentDictionary)>(); - - internal ConcurrentDictionary KeySequencesCache { get; } = new ConcurrentDictionary(); - + internal ConcurrentDictionary)> RefsToEntityQueryCache { get; } + internal ConcurrentDictionary KeySequencesCache { get; } internal ConcurrentDictionary> PersistRequestCache { get; } - = new ConcurrentDictionary>(); /// public Session OpenSession() @@ -149,6 +141,14 @@ internal StorageNode(Domain domain, NodeConfiguration configuration, ModelMappin ArgumentValidator.EnsureArgumentNotNull(mapping, nameof(mapping)); ArgumentValidator.EnsureArgumentNotNull(typeIdRegistry, nameof(typeIdRegistry)); + EntityLockProviderCache = new ConcurrentDictionary<(TypeInfo, LockMode, LockBehavior), ExecutableProvider>(); + EntityFetchQueryCache = new ConcurrentDictionary(); + EntitySetFetchQueryCache = new ConcurrentDictionary(); + EntitySetTypeStateCache = new ConcurrentDictionary(); + RefsToEntityQueryCache = new ConcurrentDictionary)>(); + KeySequencesCache = new ConcurrentDictionary(); + PersistRequestCache = new ConcurrentDictionary>(); + this.domain = domain; Configuration = configuration; Mapping = mapping; From 1943e18090045604c2e926bcfb7210ac5e28d360 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Mon, 13 Dec 2021 11:36:44 -0800 Subject: [PATCH 09/12] Update Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs Co-authored-by: Alexey Kulakov --- .../Orm/Internals/Prefetch/EntityGroupTask.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs index 96870e7282..f4b745d900 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs @@ -65,15 +65,15 @@ internal sealed class EntityGroupTask : IEquatable private static readonly Func CreateRecordSet = cachingKey => { var selectedColumnIndexes = cachingKey.ColumnIndexes; - var privaryIndex = cachingKey.Type.Indexes.PrimaryIndex; - var keyColumnsCount = privaryIndex.KeyColumns.Count; + var primaryIndex = cachingKey.Type.Indexes.PrimaryIndex; + var keyColumnsCount = primaryIndex.KeyColumns.Count; var keyColumnIndexes = new int[keyColumnsCount]; foreach (var index in Enumerable.Range(0, keyColumnsCount)) { keyColumnIndexes[index] = index; } - var columnCollectionLength = privaryIndex.Columns.Count; - return privaryIndex.GetQuery().Include(IncludeAlgorithm.ComplexCondition, + var columnCollectionLength = primaryIndex.Columns.Count; + return primaryIndex.GetQuery().Include(IncludeAlgorithm.ComplexCondition, true, context => context.GetValue(includeParameter), $"includeColumnName-{Guid.NewGuid()}", keyColumnIndexes).Filter(t => t.GetValue(columnCollectionLength)).Select(selectedColumnIndexes); }; From f1810f6ec0f482da8b182be562e801f9cf5643c4 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Dec 2021 23:13:17 -0800 Subject: [PATCH 10/12] Update Orm/Xtensive.Orm/Orm/Entity.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Orm/Entity.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Entity.cs b/Orm/Xtensive.Orm/Orm/Entity.cs index 5d5aafc5f8..0c65643a1b 100644 --- a/Orm/Xtensive.Orm/Orm/Entity.cs +++ b/Orm/Xtensive.Orm/Orm/Entity.cs @@ -271,7 +271,7 @@ public void Lock(LockMode lockMode, LockBehavior lockBehavior) { var parameterContext = new ParameterContext(); parameterContext.SetValue(keyParameter, Key.Value); - var source = Session.StorageNode.EntityLockProviderCache .GetOrAdd((TypeInfo, lockMode, lockBehavior), ExecutableProviderGenerator, Session); + var source = Session.StorageNode.EntityLockProviderCache.GetOrAdd((TypeInfo, lockMode, lockBehavior), ExecutableProviderGenerator, Session); using var recordSetReader = source.GetRecordSetReader(Session, parameterContext); recordSetReader.MoveNext(); } From 8728dfa9a87dacc14281ef6ea6afa4287d8101f9 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Dec 2021 23:13:27 -0800 Subject: [PATCH 11/12] Update Orm/Xtensive.Orm/Orm/EntitySetBase.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Orm/EntitySetBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs index 9469c8d219..e8c2795ffa 100644 --- a/Orm/Xtensive.Orm/Orm/EntitySetBase.cs +++ b/Orm/Xtensive.Orm/Orm/EntitySetBase.cs @@ -924,7 +924,7 @@ private static Key GetOwnerKey(Persistent owner) private EntitySetTypeState GetEntitySetTypeState() { EnsureOwnerIsNotRemoved(); - return Session.StorageNode.EntitySetTypeStateCache .GetOrAdd(Field, EntitySetTypeStateFactory, this); + return Session.StorageNode.EntitySetTypeStateCache.GetOrAdd(Field, EntitySetTypeStateFactory, this); } private static EntitySetTypeState BuildEntitySetTypeState(FieldInfo field, EntitySetBase entitySet) From 1d70379bb9ff97dae355303731157a44845305f8 Mon Sep 17 00:00:00 2001 From: Sergei Pavlov Date: Tue, 14 Dec 2021 23:13:35 -0800 Subject: [PATCH 12/12] Update Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs Co-authored-by: Alexey Kulakov --- Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs index 21abc20593..1eb78d9669 100644 --- a/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs +++ b/Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs @@ -176,7 +176,7 @@ private QueryTask CreateQueryTask() var session = manager.Owner.Session; var scope = new CompiledQueryProcessingScope(null, null, parameterContext, false); - QueryProvider = session.StorageNode.EntitySetFetchQueryCache .GetOrAdd(cacheKey, CreateRecordSetLoadingItems); + QueryProvider = session.StorageNode.EntitySetFetchQueryCache.GetOrAdd(cacheKey, CreateRecordSetLoadingItems); ExecutableProvider executableProvider; using (scope.Enter()) { executableProvider = session.Compile(QueryProvider);