Skip to content

Commit 1b6ff7e

Browse files
SergeiPavlovalex-kulakov
authored andcommitted
Get rid of lock in Xtensive.Orm.Internals.EntityDataReader
* ARCH-1220: Get rid of lock in Xtensive.Orm.Internals.EntityDataReader * Refactor GetPair() * in parameter * Make TypeMapping readonly struct * Work with entityMappings by reference
1 parent 2f1f0e0 commit 1b6ff7e

7 files changed

Lines changed: 116 additions & 137 deletions

File tree

Lines changed: 60 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2008-2020 Xtensive LLC.
1+
// Copyright (C) 2008-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Dmitri Maximov
@@ -19,24 +19,24 @@ namespace Xtensive.Orm.Internals
1919
{
2020
internal class EntityDataReader : DomainBound
2121
{
22-
private class RecordPartMapping
22+
private readonly struct RecordPartMapping
2323
{
24-
public int TypeIdColumnIndex { get; private set; }
25-
public Pair<int>[] Columns { get; private set; }
26-
public TypeInfo ApproximateType { get; private set; }
24+
public int TypeIdColumnIndex { get; }
25+
public IReadOnlyList<Pair<int>> Columns { get; }
26+
public TypeInfo ApproximateType { get; }
2727

28-
public RecordPartMapping(int typeIdColumnIndex, Pair<int>[] columns, TypeInfo approximateType)
28+
public RecordPartMapping(int typeIdColumnIndex, IReadOnlyList<Pair<int>> columns, TypeInfo approximateType)
2929
{
3030
TypeIdColumnIndex = typeIdColumnIndex;
3131
Columns = columns;
3232
ApproximateType = approximateType;
3333
}
3434
}
3535

36-
private class CacheItem
36+
private readonly struct CacheItem
3737
{
38-
public RecordSetHeader Header { get; private set; }
39-
public RecordPartMapping[] Mappings { get; private set; }
38+
public RecordSetHeader Header { get; }
39+
public RecordPartMapping[] Mappings { get; }
4040

4141
public CacheItem(RecordSetHeader header, RecordPartMapping[] mappings)
4242
{
@@ -46,77 +46,70 @@ public CacheItem(RecordSetHeader header, RecordPartMapping[] mappings)
4646
}
4747

4848
private readonly ICache<RecordSetHeader, CacheItem> cache;
49-
private readonly object _lock = new object();
50-
49+
5150
public IEnumerable<Record> Read(IEnumerable<Tuple> source, RecordSetHeader header, Session session)
5251
{
53-
CacheItem cacheItem;
54-
var recordPartCount = header.ColumnGroups.Count;
52+
var columns = header.Columns;
53+
var columnGroups = header.ColumnGroups;
54+
var recordPartCount = columnGroups.Count;
5555
var context = new MaterializationContext(session, recordPartCount);
56-
lock (_lock) {
57-
if (!cache.TryGetItem(header, false, out cacheItem)) {
58-
var typeIdColumnName = Domain.Handlers.NameBuilder.TypeIdColumnName;
59-
var model = context.Model;
60-
var mappings = new RecordPartMapping[recordPartCount];
61-
for (int i = 0; i < recordPartCount; i++) {
62-
var columnGroup = header.ColumnGroups[i];
63-
var approximateType = columnGroup.TypeInfoRef.Resolve(model);
64-
var columnMapping = new List<Pair<int>>();
65-
var typeIdColumnIndex = -1;
66-
foreach (var columnIndex in columnGroup.Columns) {
67-
var column = (MappedColumn) header.Columns[columnIndex];
68-
var columnInfo = column.ColumnInfoRef.Resolve(model);
69-
FieldInfo fieldInfo;
70-
if (!approximateType.Fields.TryGetValue(columnInfo.Field.Name, out fieldInfo))
71-
continue;
56+
if (!cache.TryGetItem(header, false, out var cacheItem)) {
57+
var typeIdColumnName = Domain.Handlers.NameBuilder.TypeIdColumnName;
58+
var model = context.Model;
59+
var mappings = new RecordPartMapping[recordPartCount];
60+
for (int i = 0; i < recordPartCount; i++) {
61+
var columnGroup = columnGroups[i];
62+
var approximateType = columnGroup.TypeInfoRef.Resolve(model);
63+
var columnMapping = new List<Pair<int>>(columnGroup.Columns.Count);
64+
var typeIdColumnIndex = -1;
65+
foreach (var columnIndex in columnGroup.Columns) {
66+
var column = (MappedColumn) columns[columnIndex];
67+
var columnInfo = column.ColumnInfoRef.Resolve(model);
68+
if (approximateType.Fields.TryGetValue(columnInfo.Field.Name, out var fieldInfo)) {
7269
var targetColumnIndex = fieldInfo.MappingInfo.Offset;
73-
if (columnInfo.Name==typeIdColumnName)
70+
if (columnInfo.Name == typeIdColumnName) {
7471
typeIdColumnIndex = column.Index;
72+
}
7573
columnMapping.Add(new Pair<int>(targetColumnIndex, columnIndex));
7674
}
77-
mappings[i] = new RecordPartMapping(typeIdColumnIndex, columnMapping.ToArray(), approximateType);
7875
}
79-
cacheItem = new CacheItem(header, mappings);
80-
cache.Add(cacheItem);
76+
mappings[i] = new RecordPartMapping(typeIdColumnIndex, columnMapping, approximateType);
8177
}
78+
cacheItem = new CacheItem(header, mappings);
79+
cache.Add(cacheItem);
8280
}
8381
return source.Select(tuple => ParseRow(tuple, context, cacheItem.Mappings));
8482
}
8583

86-
private Record ParseRow(Tuple tuple, MaterializationContext context, RecordPartMapping[] recordPartMappings)
87-
{
88-
var count = recordPartMappings.Length;
89-
90-
if (count==1)
91-
return new Record(tuple, ParseColumnGroup(tuple, context, 0, recordPartMappings[0]));
84+
private Record ParseRow(Tuple tuple, MaterializationContext context, RecordPartMapping[] recordPartMappings) =>
85+
recordPartMappings.Length == 1
86+
? new Record(tuple, ParseColumnGroup(tuple, context, 0, recordPartMappings[0]))
87+
: new Record(tuple, recordPartMappings.Select(
88+
(recordPartMapping, i) => ParseColumnGroup(tuple, context, i, recordPartMapping))
89+
);
9290

93-
var pairs = new List<Pair<Key, Tuple>>(
94-
recordPartMappings
95-
.Select((recordPartMapping, i) => ParseColumnGroup(tuple, context, i, recordPartMapping)));
96-
return new Record(tuple, pairs);
97-
}
98-
99-
private Pair<Key, Tuple> ParseColumnGroup(Tuple tuple, MaterializationContext context, int groupIndex, RecordPartMapping mapping)
91+
private Pair<Key, Tuple> ParseColumnGroup(Tuple tuple, MaterializationContext context, int groupIndex, in RecordPartMapping mapping)
10092
{
101-
TypeReferenceAccuracy accuracy;
102-
int typeId = ExtractTypeId(mapping.ApproximateType, context.TypeIdRegistry, tuple, mapping.TypeIdColumnIndex, out accuracy);
103-
var typeMapping = typeId==TypeInfo.NoTypeId ? null : context.GetTypeMapping(groupIndex, mapping.ApproximateType, typeId, mapping.Columns);
104-
if (typeMapping==null)
93+
int typeId = ExtractTypeId(mapping.ApproximateType, context.TypeIdRegistry, tuple, mapping.TypeIdColumnIndex, out var accuracy);
94+
if (typeId == TypeInfo.NoTypeId) {
10595
return new Pair<Key, Tuple>(null, null);
106-
107-
bool canCache = accuracy==TypeReferenceAccuracy.ExactType;
108-
Key key;
109-
if (typeMapping.KeyTransform.Descriptor.Count <= WellKnown.MaxGenericKeyLength)
110-
key = KeyFactory.Materialize(Domain, context.Session.StorageNodeId, typeMapping.Type, tuple, accuracy, canCache, typeMapping.KeyIndexes);
111-
else {
112-
var keyTuple = typeMapping.KeyTransform.Apply(TupleTransformType.TransformedTuple, tuple);
113-
key = KeyFactory.Materialize(Domain, context.Session.StorageNodeId, typeMapping.Type, keyTuple, accuracy, canCache, null);
11496
}
115-
if (accuracy == TypeReferenceAccuracy.ExactType) {
116-
var entityTuple = typeMapping.Transform.Apply(TupleTransformType.Tuple, tuple);
117-
return new Pair<Key, Tuple>(key, entityTuple);
97+
var typeMapping = context.GetTypeMapping(groupIndex, mapping.ApproximateType, typeId, mapping.Columns);
98+
99+
bool canCache = accuracy == TypeReferenceAccuracy.ExactType;
100+
var keyTuple = tuple;
101+
var keyIndexes = typeMapping.KeyIndexes;
102+
if (typeMapping.KeyTransform.Descriptor.Count > WellKnown.MaxGenericKeyLength) {
103+
keyTuple = typeMapping.KeyTransform.Apply(TupleTransformType.TransformedTuple, tuple);
104+
keyIndexes = null;
118105
}
119-
return new Pair<Key, Tuple>(key, null);
106+
var key = KeyFactory.Materialize(Domain, context.Session.StorageNodeId, typeMapping.Type, keyTuple, accuracy, canCache, keyIndexes);
107+
return new Pair<Key, Tuple>(
108+
key,
109+
accuracy == TypeReferenceAccuracy.ExactType
110+
? typeMapping.Transform.Apply(TupleTransformType.Tuple, tuple)
111+
: null
112+
);
120113
}
121114

122115
public static int ExtractTypeId(TypeInfo type, TypeIdRegistry typeIdRegistry, Tuple tuple, int typeIdIndex, out TypeReferenceAccuracy accuracy)
@@ -146,8 +139,10 @@ public static int ExtractTypeId(TypeInfo type, TypeIdRegistry typeIdRegistry, Tu
146139
internal EntityDataReader(Domain domain)
147140
: base(domain)
148141
{
149-
cache = new LruCache<RecordSetHeader, CacheItem>(domain.Configuration.RecordSetMappingCacheSize,
150-
m => m.Header, new WeakestCache<RecordSetHeader, CacheItem>(false, false, m => m.Header));
142+
cache = new FastConcurrentLruCache<RecordSetHeader, CacheItem>(
143+
domain.Configuration.RecordSetMappingCacheSize,
144+
m => m.Header
145+
);
151146
}
152147
}
153-
}
148+
}

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexander Nikolaev
@@ -161,13 +161,11 @@ private void PutLoadedStatesInCache(IEnumerable<Tuple> queryResult, EntityDataRe
161161
{
162162
var entityRecords = reader.Read(queryResult, Provider.Header, manager.Owner.Session);
163163
foreach (var entityRecord in entityRecords) {
164-
if (entityRecord != null) {
165-
var fetchedKey = entityRecord.GetKey();
166-
var tuple = entityRecord.GetTuple();
167-
if (tuple != null) {
168-
manager.SaveStrongReference(manager.Owner.UpdateState(fetchedKey, tuple));
169-
foundedKeys.Add(fetchedKey);
170-
}
164+
var fetchedKey = entityRecord.GetKey();
165+
var tuple = entityRecord.GetTuple();
166+
if (tuple != null) {
167+
manager.SaveStrongReference(manager.Owner.UpdateState(fetchedKey, tuple));
168+
foundedKeys.Add(fetchedKey);
171169
}
172170
}
173171
}

Orm/Xtensive.Orm/Orm/Internals/TypeMapping.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace Xtensive.Orm.Internals
1111
{
12-
internal class TypeMapping
12+
internal readonly struct TypeMapping
1313
{
1414
public readonly TypeInfo Type;
1515
public readonly MapTransform KeyTransform;

Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationContext.cs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal sealed class MaterializationContext
2020

2121
private struct EntityMappingCache
2222
{
23-
public TypeMapping SingleItem;
23+
public TypeMapping? SingleItem;
2424
public Dictionary<int, TypeMapping> Items;
2525
}
2626

@@ -58,33 +58,37 @@ public TypeIdRegistry TypeIdRegistry
5858
/// </summary>
5959
public Queue<Action> MaterializationQueue { get; set; }
6060

61-
public TypeMapping GetTypeMapping(int entityIndex, TypeInfo approximateType, int typeId, Pair<int>[] columns)
61+
public TypeMapping GetTypeMapping(int entityIndex, TypeInfo approximateType, int typeId, IReadOnlyList<Pair<int>> columns)
6262
{
6363
TypeMapping result;
64-
var cache = entityMappings[entityIndex];
65-
if (cache.SingleItem!=null) {
66-
if (typeId!=ResolveTypeToNodeSpecificTypeIdentifier(cache.SingleItem.Type))
64+
ref var cache = ref entityMappings[entityIndex];
65+
if (cache.SingleItem != null) {
66+
if (typeId != ResolveTypeToNodeSpecificTypeIdentifier(cache.SingleItem?.Type)) {
6767
throw new ArgumentOutOfRangeException("typeId");
68-
return cache.SingleItem;
68+
}
69+
return cache.SingleItem.Value;
6970
}
7071
if (cache.Items.TryGetValue(typeId, out result))
7172
return result;
7273

73-
var type = TypeIdRegistry[typeId];
74-
var keyInfo = type.Key;
74+
var type = TypeIdRegistry[typeId];
75+
var keyInfo = type.Key;
7576
var descriptor = type.TupleDescriptor;
7677

77-
var typeColumnMap = columns.ToArray();
78-
if (approximateType.IsInterface)
78+
var typeColumnMap = columns;
79+
if (approximateType.IsInterface) {
7980
// fixup target index
80-
for (int i = 0; i < columns.Length; i++) {
81-
var pair = typeColumnMap[i];
81+
var newColumns = new Pair<int>[columns.Count];
82+
for (int i = columns.Count; i-- > 0;) {
83+
var pair = columns[i];
8284
var approxTargetIndex = pair.First;
8385
var interfaceField = approximateType.Columns[approxTargetIndex].Field;
8486
var field = type.FieldMap[interfaceField];
8587
var targetIndex = field.MappingInfo.Offset;
86-
typeColumnMap[i] = new Pair<int>(targetIndex, pair.Second);
88+
newColumns[i] = new Pair<int>(targetIndex, pair.Second);
8789
}
90+
typeColumnMap = newColumns;
91+
}
8892

8993
int[] allIndexes = MaterializationHelper.CreateSingleSourceMap(descriptor.Count, typeColumnMap);
9094
int[] keyIndexes = allIndexes.Take(keyInfo.TupleDescriptor.Count).ToArray();
@@ -98,8 +102,7 @@ public TypeMapping GetTypeMapping(int entityIndex, TypeInfo approximateType, int
98102
cache.SingleItem = result;
99103
else
100104
cache.Items.Add(typeId, result);
101-
entityMappings[entityIndex] = cache;
102-
105+
103106
return result;
104107
}
105108

@@ -109,7 +112,7 @@ private int ResolveTypeToNodeSpecificTypeIdentifier(TypeInfo typeInfo)
109112
return TypeIdRegistry[typeInfo];
110113
}
111114

112-
115+
113116
// Constructors
114117

115118
public MaterializationContext(Session session, int entityCount)

Orm/Xtensive.Orm/Orm/Linq/Materialization/MaterializationHelper.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,17 @@ internal static class MaterializationHelper
4343
public static readonly MethodInfo PrefetchEntitySetMethodInfo = typeof(MaterializationHelper)
4444
.GetMethod(nameof(PrefetechEntitySet), BindingFlags.Public | BindingFlags.Static);
4545

46-
public static int[] CreateSingleSourceMap(int targetLength, Pair<int>[] remappedColumns)
46+
public static int[] CreateSingleSourceMap(int targetLength, IReadOnlyList<Pair<int>> remappedColumns)
4747
{
4848
var map = new int[targetLength];
4949
for (var i = 0; i < map.Length; i++) {
5050
map[i] = MapTransform.NoMapping;
5151
}
5252

53-
for (var i = 0; i < remappedColumns.Length; i++) {
54-
var targetIndex = remappedColumns[i].First;
55-
var sourceIndex = remappedColumns[i].Second;
53+
for (var i = 0; i < remappedColumns.Count; i++) {
54+
var remappedColumn = remappedColumns[i];
55+
var targetIndex = remappedColumn.First;
56+
var sourceIndex = remappedColumn.Second;
5657
map[targetIndex] = sourceIndex;
5758
}
5859

@@ -114,8 +115,8 @@ public static TEntitySet PrefetechEntitySet<TEntitySet>(TEntitySet entitySet, It
114115
where TEntitySet : EntitySetBase
115116
{
116117
context.Session.Handler.Prefetch(
117-
entitySet.Owner.Key,
118-
entitySet.Owner.TypeInfo,
118+
entitySet.Owner.Key,
119+
entitySet.Owner.TypeInfo,
119120
new List<PrefetchFieldDescriptor>{new PrefetchFieldDescriptor(entitySet.Field, WellKnown.EntitySetPreloadCount)});
120121
return entitySet;
121122
}

0 commit comments

Comments
 (0)