Skip to content

Commit b2d78f2

Browse files
authored
Make StorageNode.EntityFetchQueryCache per-Domain (#361)
* Make `StorageNode.EntityFetchQueryCache` static * Make `EntitySetFetchQueryCache` static * Make `RefsToEntityQueryCache` static * Make caches per-domain * Optimize `GetColumnsToBeLoaded()` * Make `RootContainerCacheEntry` record struct * EntityLockProviderCache * EntitySetTypeStateCache * Make `PersistenceState` byte enum * Make `TypeReferenceAccuracy` byte enum * Refactor EntitySetTypeState * This reverts commit d282187. * Revert "EntityLockProviderCache" This reverts commit 85d7c8c. * byte enums
1 parent eb45801 commit b2d78f2

16 files changed

+101
-190
lines changed

Orm/Xtensive.Orm/Orm/Attributes/KeyGeneratorKind.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Xtensive.Orm
99
/// <summary>
1010
/// Specifies key generator type to use for a particular hierarchy.
1111
/// </summary>
12-
public enum KeyGeneratorKind
12+
public enum KeyGeneratorKind : byte
1313
{
1414
/// <summary>
1515
/// No key generator is provided for hierarchy.
@@ -35,4 +35,4 @@ public enum KeyGeneratorKind
3535
/// </summary>
3636
Custom = 2
3737
}
38-
}
38+
}

Orm/Xtensive.Orm/Orm/Domain.cs

+16
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
using Xtensive.Orm.Logging;
2424
using Xtensive.Orm.Model;
2525
using Xtensive.Orm.Providers;
26+
using Xtensive.Orm.Rse.Providers;
2627
using Xtensive.Orm.Upgrade;
2728
using Xtensive.Sql;
2829
using Xtensive.Sql.Info;
@@ -134,6 +135,21 @@ public static Domain Demand()
134135

135136
internal ConcurrentDictionary<Type, System.Linq.Expressions.MethodCallExpression> RootCallExpressionsCache { get; } = new();
136137

138+
/// <summary>
139+
/// Caches uncompiled queries used by <see cref="PrefetchManager"/> to fetch certain entities.
140+
/// </summary>
141+
internal ConcurrentDictionary<RecordSetCacheKey, CompilableProvider> EntityFetchQueryCache { get; } = new();
142+
143+
/// <summary>
144+
/// Caches uncompiled queries used by <see cref="PrefetchManager"/> to fetch <see cref="EntitySet{TItem}"/> content.
145+
/// </summary>
146+
internal ConcurrentDictionary<ItemsQueryCacheKey, CompilableProvider> EntitySetFetchQueryCache { get; } = new();
147+
148+
/// <summary>
149+
/// Caches queries that get references to entities for certain association.
150+
/// </summary>
151+
internal ConcurrentDictionary<AssociationInfo, (CompilableProvider, Parameter<Xtensive.Tuples.Tuple>)> RefsToEntityQueryCache { get; } = new();
152+
137153
internal object UpgradeContextCookie { get; private set; }
138154

139155
internal SqlConnection SingleConnection { get; private set; }

Orm/Xtensive.Orm/Orm/DomainBound.cs

+36-49
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,47 @@
44
// Created by: Dmitri Maximov
55
// Created: 2007.08.10
66

7-
using System;
87
using Xtensive.Core;
98

10-
using Xtensive.IoC;
11-
using Xtensive.Orm;
129

13-
namespace Xtensive.Orm
10+
namespace Xtensive.Orm;
11+
12+
/// <summary>
13+
/// Base class for all objects that are bound to the <see cref="Domain"/> instance.
14+
/// </summary>
15+
public abstract class DomainBound: IContextBound<Domain>
1416
{
1517
/// <summary>
16-
/// Base class for all objects that are bound to the <see cref="Domain"/> instance.
18+
/// Gets <see cref="Domain"/> to which current instance is bound.
19+
/// </summary>
20+
public Domain Domain { get; internal set; }
21+
22+
#region IContextBound<Domain> Members
23+
24+
/// <inheritdoc/>
25+
Domain IContextBound<Domain>.Context => Domain;
26+
27+
#endregion
28+
29+
30+
// Constructors
31+
32+
/// <summary>
33+
/// Initializes a new instance of this class.
34+
/// </summary>
35+
protected DomainBound()
36+
{
37+
}
38+
39+
/// <summary>
40+
/// Initializes a new instance of this class.
1741
/// </summary>
18-
public abstract class DomainBound: IContextBound<Domain>
42+
/// <param name="domain"><see cref="Orm.Domain"/>, to which current instance
43+
/// is bound.</param>
44+
/// <exception cref="ArgumentNullException"><paramref name="domain"/> is <see langword="null" />.</exception>
45+
protected DomainBound(Domain domain)
1946
{
20-
private Domain domain;
21-
22-
/// <summary>
23-
/// Gets <see cref="Domain"/> to which current instance is bound.
24-
/// </summary>
25-
public Domain Domain
26-
{
27-
get { return domain; }
28-
internal set { domain = value; }
29-
}
30-
31-
#region IContextBound<Domain> Members
32-
33-
/// <inheritdoc/>
34-
Domain IContextBound<Domain>.Context
35-
{
36-
get { return domain; }
37-
}
38-
39-
#endregion
40-
41-
42-
// Constructors
43-
44-
/// <summary>
45-
/// Initializes a new instance of this class.
46-
/// </summary>
47-
protected DomainBound()
48-
{
49-
}
50-
51-
/// <summary>
52-
/// Initializes a new instance of this class.
53-
/// </summary>
54-
/// <param name="domain"><see cref="Orm.Domain"/>, to which current instance
55-
/// is bound.</param>
56-
/// <exception cref="ArgumentNullException"><paramref name="domain"/> is <see langword="null" />.</exception>
57-
protected DomainBound(Domain domain)
58-
{
59-
ArgumentNullException.ThrowIfNull(domain);
60-
this.domain = domain;
61-
}
47+
ArgumentNullException.ThrowIfNull(domain);
48+
Domain = domain;
6249
}
63-
}
50+
}

Orm/Xtensive.Orm/Orm/Internals/EntitySetTypeState.cs

+8-26
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,16 @@
44
// Created by: Alexander Nikolaev
55
// Created: 2009.08.04
66

7-
using System;
8-
using Xtensive.Tuples;
9-
using Xtensive.Orm.Providers;
107
using Xtensive.Orm.Rse.Providers;
118
using Tuple = Xtensive.Tuples.Tuple;
129
using Xtensive.Tuples.Transform;
13-
using Xtensive.Orm.Rse;
1410

15-
namespace Xtensive.Orm.Internals
16-
{
17-
[Serializable]
18-
internal sealed class EntitySetTypeState
19-
{
20-
public readonly ExecutableProvider SeekProvider;
11+
namespace Xtensive.Orm.Internals;
2112

22-
public readonly MapTransform SeekTransform;
23-
24-
public readonly Func<Tuple, Entity> ItemCtor;
25-
26-
public readonly Func<QueryEndpoint,long> ItemCountQuery;
27-
28-
public EntitySetTypeState(ExecutableProvider seekProvider, MapTransform seekTransform,
29-
Func<Tuple, Entity> itemCtor, Func<QueryEndpoint, long> itemCountQuery)
30-
{
31-
SeekProvider = seekProvider;
32-
SeekTransform = seekTransform;
33-
ItemCtor = itemCtor;
34-
ItemCountQuery = itemCountQuery;
35-
}
36-
}
37-
}
13+
[Serializable]
14+
internal record EntitySetTypeState(
15+
ExecutableProvider SeekProvider,
16+
MapTransform SeekTransform,
17+
Func<Tuple, Entity> ItemCtor,
18+
Func<QueryEndpoint, long> ItemCountQuery
19+
);

Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntityGroupTask.cs

+10-13
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
// Created by: Alexander Nikolaev
55
// Created: 2009.10.20
66

7-
using System;
8-
using System.Collections.Generic;
9-
using System.Linq;
107
using Xtensive.Collections;
118
using Xtensive.Core;
129
using Xtensive.Orm.Model;
@@ -33,11 +30,17 @@ public override bool Equals(object obj) =>
3330

3431
// Constructors
3532

36-
public RecordSetCacheKey(IReadOnlyList<ColNum> columnIndexes, TypeInfo type, int cachedHashCode)
33+
public RecordSetCacheKey(IReadOnlyList<ColNum> columnIndexes, TypeInfo type)
3734
{
3835
ColumnIndexes = columnIndexes;
3936
Type = type;
40-
this.cachedHashCode = cachedHashCode;
37+
38+
HashCode hashCode = new();
39+
foreach (var columnIndex in columnIndexes) {
40+
hashCode.Add(columnIndex);
41+
}
42+
hashCode.Add(type);
43+
cachedHashCode = hashCode.ToHashCode();
4144
}
4245
}
4346

@@ -125,7 +128,7 @@ private QueryTask CreateQueryTask(List<Tuple> currentKeySet)
125128
var parameterContext = new ParameterContext();
126129
parameterContext.SetValue(includeParameter, currentKeySet);
127130
var session = manager.Owner.Session;
128-
Provider = session.StorageNode.EntityFetchQueryCache.GetOrAdd(cacheKey, CreateRecordSet);
131+
Provider = manager.Owner.Session.Domain.EntityFetchQueryCache.GetOrAdd(cacheKey, static k => CreateRecordSet(k));
129132
if (session.Domain.TagsEnabled && session.Tags != null) {
130133
foreach (var tag in session.Tags) {
131134
Provider = new TagProvider(Provider, tag);
@@ -192,13 +195,7 @@ public EntityGroupTask(TypeInfo type, IReadOnlyList<ColNum> columnIndexes, Prefe
192195

193196
this.type = type;
194197
this.manager = manager;
195-
var cachedHashCode = 0;
196-
foreach (var columnIndex in columnIndexes) {
197-
cachedHashCode = unchecked (379 * cachedHashCode + columnIndex);
198-
}
199-
200-
cachedHashCode ^= type.GetHashCode();
201-
cacheKey = new RecordSetCacheKey(columnIndexes, type, cachedHashCode);
198+
cacheKey = new RecordSetCacheKey(columnIndexes, type);
202199
}
203200
}
204201
}

Orm/Xtensive.Orm/Orm/Internals/Prefetch/EntitySetTask.cs

+1-6
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
// Created by: Alexander Nikolaev
55
// Created: 2009.09.09
66

7-
using System;
8-
using System.Collections.Generic;
9-
using System.Linq;
10-
using System.Linq.Expressions;
11-
using System.Reflection;
127
using Xtensive.Core;
138
using Xtensive.Orm.Linq;
149
using Xtensive.Orm.Model;
@@ -173,7 +168,7 @@ private QueryTask CreateQueryTask()
173168

174169
var session = manager.Owner.Session;
175170
var scope = new CompiledQueryProcessingScope(null, null, parameterContext, false);
176-
QueryProvider = session.StorageNode.EntitySetFetchQueryCache.GetOrAdd(cacheKey, CreateRecordSetLoadingItems);
171+
QueryProvider = session.Domain.EntitySetFetchQueryCache.GetOrAdd(cacheKey, static k => CreateRecordSetLoadingItems(k));
177172
if (session.Domain.TagsEnabled && session.Tags != null) {
178173
foreach (var tag in session.Tags) {
179174
QueryProvider = new TagProvider(QueryProvider, tag);

Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchHelper.cs

+5-10
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,9 @@ public static bool AddColumns(IEnumerable<ColumnInfo> candidateColumns,
7575
return result;
7676
}
7777

78-
public static List<ColNum> GetColumnsToBeLoaded(SortedDictionary<ColNum, ColumnInfo> userColumnIndexes,
79-
TypeInfo type)
80-
{
81-
var result = new List<ColNum>(userColumnIndexes.Count);
82-
result.AddRange(type.Indexes.PrimaryIndex.ColumnIndexMap.System);
83-
result.AddRange(userColumnIndexes.Where(pair => !pair.Value.IsPrimaryKey
84-
&& !pair.Value.IsSystem).Select(pair => pair.Key));
85-
return result;
86-
}
78+
public static IReadOnlyList<ColNum> GetColumnsToBeLoaded(SortedDictionary<ColNum, ColumnInfo> userColumnIndexes, TypeInfo type) =>
79+
type.Indexes.PrimaryIndex.ColumnIndexMap.System
80+
.Concat(userColumnIndexes.Where(pair => !pair.Value.IsPrimaryKey && !pair.Value.IsSystem).Select(pair => pair.Key))
81+
.ToArray();
8782
}
88-
}
83+
}

Orm/Xtensive.Orm/Orm/Internals/Prefetch/PrefetchManager.cs

+6-20
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,11 @@ public RootContainerCacheKey(TypeInfo type, IEnumerable<PrefetchFieldDescriptor>
4949
}
5050
}
5151

52-
private class RootContainerCacheEntry
53-
{
54-
public readonly RootContainerCacheKey Key;
55-
56-
public readonly SortedDictionary<ColNum, ColumnInfo> Columns;
57-
58-
public readonly IReadOnlyList<ColNum> ColumnsToBeLoaded;
59-
60-
61-
// Constructors
62-
63-
public RootContainerCacheEntry(in RootContainerCacheKey key, SortedDictionary<ColNum, ColumnInfo> columns,
64-
IReadOnlyList<ColNum> columnsToBeLoaded)
65-
{
66-
Key = key;
67-
Columns = columns;
68-
ColumnsToBeLoaded = columnsToBeLoaded;
69-
}
70-
}
52+
private record struct RootContainerCacheEntry(
53+
RootContainerCacheKey Key,
54+
SortedDictionary<ColNum, ColumnInfo> Columns,
55+
IReadOnlyList<ColNum> ColumnsToBeLoaded
56+
);
7157

7258
#endregion
7359

@@ -257,7 +243,7 @@ public void GetCachedColumnIndexes(TypeInfo type,
257243
{
258244
var cacheKey = new RootContainerCacheKey(type, descriptors);
259245
var cacheEntry = columnsCache[cacheKey, true];
260-
if (cacheEntry == null) {
246+
if (cacheEntry == default) {
261247
columns = PrefetchHelper.GetColumns(ExtractColumns(descriptors),type);
262248
columnsToBeLoaded = PrefetchHelper.GetColumnsToBeLoaded(columns, type);
263249
cacheEntry = new RootContainerCacheEntry(cacheKey, columns, columnsToBeLoaded);

Orm/Xtensive.Orm/Orm/Model/InheritanceSchema.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Xtensive.Orm.Model
1010
/// Enumerates all supported 'class to tables mapping' schemes.
1111
/// </summary>
1212
/// <remarks>See M.Fowler - "Patterns of Enterprise Application Architecture".</remarks>
13-
public enum InheritanceSchema
13+
public enum InheritanceSchema : byte
1414
{
1515
/// <summary>
1616
/// Is equal to <see cref="ClassTable"/>.
@@ -30,4 +30,4 @@ public enum InheritanceSchema
3030
/// </summary>
3131
ConcreteTable = 2
3232
}
33-
}
33+
}

Orm/Xtensive.Orm/Orm/PersistenceState.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Xtensive.Orm
99
/// <summary>
1010
/// Defines possible persistence states of the entities.
1111
/// </summary>
12-
public enum PersistenceState
12+
public enum PersistenceState : byte
1313
{
1414
/// <summary>
1515
/// The entity is synchronized with the database (there are no unsaved changes).
@@ -28,4 +28,4 @@ public enum PersistenceState
2828
/// </summary>
2929
Removed = 3,
3030
}
31-
}
31+
}

Orm/Xtensive.Orm/Orm/Providers/SessionHandler.References.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public virtual IEnumerable<ReferenceInfo> GetReferencesTo(Entity target, Associa
2929
{
3030
if (association.IsPaired)
3131
return FindReferences(target, association, true);
32-
var (recordSet, parameter) = Session.StorageNode.RefsToEntityQueryCache .GetOrAdd(association, BuildReferencingQuery);
32+
var (recordSet, parameter) = Session.Domain.RefsToEntityQueryCache.GetOrAdd(association, static k => BuildReferencingQuery(k));
3333
var parameterContext = new ParameterContext();
3434
parameterContext.SetValue(parameter, target.Key.Value);
3535
ExecutableProvider executableProvider = Session.Compile(recordSet);

Orm/Xtensive.Orm/Orm/Providers/SessionHandler.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public abstract partial class SessionHandler : IDisposable, IAsyncDisposable
2222
/// <summary>
2323
/// Gets <see cref="HandlerAccessor"/>.
2424
/// </summary>
25-
protected HandlerAccessor Handlers { get; private set; }
25+
protected HandlerAccessor Handlers => Session.Handlers;
2626

2727
/// <summary>
2828
/// Gets the current <see cref="Session"/>.
@@ -117,7 +117,6 @@ public virtual void Dispose()
117117
protected SessionHandler(Session session)
118118
{
119119
Session = session;
120-
Handlers = session.Handlers;
121120
}
122121
}
123-
}
122+
}

0 commit comments

Comments
 (0)