Skip to content

Commit 0747d74

Browse files
Reduces check session and set context id redundant calls
* Reduces object allocations.
1 parent 7dc47e0 commit 0747d74

31 files changed

+743
-798
lines changed

src/NHibernate.Test/Async/NHSpecificTest/Logs/LogsFixture.cs

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

1010

1111
using System.Collections;
12-
12+
using System.Collections.Concurrent;
13+
using System.Linq;
14+
using System.Threading;
1315
using NHibernate.Impl;
1416

1517
using NUnit.Framework;
@@ -56,6 +58,62 @@ public async Task WillGetSessionIdFromSessionLogsAsync()
5658
}
5759
}
5860

61+
[Test]
62+
public async Task WillGetSessionIdFromSessionLogsConcurrentAsync()
63+
{
64+
GlobalContext.Properties["sessionId"] = new SessionIdCapturer();
65+
66+
var semaphore = new ManualResetEventSlim();
67+
var failures = new ConcurrentBag<Exception>();
68+
var sessionIds = new ConcurrentDictionary<int, Guid>();
69+
var array = Enumerable.Range(1, 10).Select(
70+
i => new Thread(
71+
() =>
72+
{
73+
try
74+
{
75+
using (var s = Sfi.OpenSession())
76+
{
77+
sessionIds.AddOrUpdate(
78+
i,
79+
s.GetSessionImplementation().SessionId,
80+
(ti, old) => throw new InvalidOperationException(
81+
$"Thread number {ti} has already session id {old}, while attempting to set it to" +
82+
$" {s.GetSessionImplementation().SessionId}"));
83+
semaphore.Wait();
84+
85+
for (int j = 0; j < 10; j++)
86+
{
87+
s.Get<Person>(i * 10 + j); //will execute some sql
88+
}
89+
}
90+
}
91+
catch (Exception e)
92+
{
93+
failures.Add(e);
94+
}
95+
})).ToArray();
96+
97+
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
98+
{
99+
Array.ForEach(array, thread => thread.Start());
100+
// Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently.
101+
await (Task.Delay(100));
102+
semaphore.Set();
103+
Array.ForEach(array, thread => thread.Join());
104+
105+
Assert.That(failures, Is.Empty, $"{failures.Count} thread(s) failed.");
106+
107+
var loggingEvent = spy.GetWholeLog();
108+
for (var i = 1; i < 11; i++)
109+
for (var j = 0; j < 10; j++)
110+
{
111+
var sessionId = sessionIds[i];
112+
Assert.That(loggingEvent, Does.Contain($"p0 = {i * 10 + j} [Type: Int32 (0:0:0)] | SessionId: {sessionId}"));
113+
}
114+
}
115+
}
116+
59117
// IFixingRequired interface ensures the value is evaluated at log time rather than at log buffer flush time.
60118
public class SessionIdCapturer : IFixingRequired
61119
{

src/NHibernate.Test/NHSpecificTest/Logs/LogsFixture.cs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.Collections;
2-
2+
using System.Collections.Concurrent;
3+
using System.Linq;
4+
using System.Threading;
35
using NHibernate.Impl;
46

57
using NUnit.Framework;
@@ -45,6 +47,62 @@ public void WillGetSessionIdFromSessionLogs()
4547
}
4648
}
4749

50+
[Test]
51+
public void WillGetSessionIdFromSessionLogsConcurrent()
52+
{
53+
GlobalContext.Properties["sessionId"] = new SessionIdCapturer();
54+
55+
var semaphore = new ManualResetEventSlim();
56+
var failures = new ConcurrentBag<Exception>();
57+
var sessionIds = new ConcurrentDictionary<int, Guid>();
58+
var array = Enumerable.Range(1, 10).Select(
59+
i => new Thread(
60+
() =>
61+
{
62+
try
63+
{
64+
using (var s = Sfi.OpenSession())
65+
{
66+
sessionIds.AddOrUpdate(
67+
i,
68+
s.GetSessionImplementation().SessionId,
69+
(ti, old) => throw new InvalidOperationException(
70+
$"Thread number {ti} has already session id {old}, while attempting to set it to" +
71+
$" {s.GetSessionImplementation().SessionId}"));
72+
semaphore.Wait();
73+
74+
for (int j = 0; j < 10; j++)
75+
{
76+
s.Get<Person>(i * 10 + j); //will execute some sql
77+
}
78+
}
79+
}
80+
catch (Exception e)
81+
{
82+
failures.Add(e);
83+
}
84+
})).ToArray();
85+
86+
using (var spy = new TextLogSpy("NHibernate.SQL", "%message | SessionId: %property{sessionId}"))
87+
{
88+
Array.ForEach(array, thread => thread.Start());
89+
// Give some time to threads for reaching the wait, having all of them ready to do most of their job concurrently.
90+
Thread.Sleep(100);
91+
semaphore.Set();
92+
Array.ForEach(array, thread => thread.Join());
93+
94+
Assert.That(failures, Is.Empty, $"{failures.Count} thread(s) failed.");
95+
96+
var loggingEvent = spy.GetWholeLog();
97+
for (var i = 1; i < 11; i++)
98+
for (var j = 0; j < 10; j++)
99+
{
100+
var sessionId = sessionIds[i];
101+
Assert.That(loggingEvent, Does.Contain($"p0 = {i * 10 + j} [Type: Int32 (0:0:0)] | SessionId: {sessionId}"));
102+
}
103+
}
104+
}
105+
48106
// IFixingRequired interface ensures the value is evaluated at log time rather than at log buffer flush time.
49107
public class SessionIdCapturer : IFixingRequired
50108
{

src/NHibernate/Async/Engine/ISessionImplementor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace NHibernate.Engine
2828
{
2929
using System.Threading.Tasks;
3030
using System.Threading;
31+
3132
public partial interface ISessionImplementor
3233
{
3334

src/NHibernate/Async/Event/IEventSource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public partial interface IEventSource : ISessionImplementor, ISession
3535

3636
/// <summary> Cascade refresh an entity instance</summary>
3737
Task RefreshAsync(object obj, IDictionary refreshedAlready, CancellationToken cancellationToken);
38-
38+
3939
/// <summary> Cascade delete an entity instance</summary>
4040
Task DeleteAsync(string entityName, object child, bool isCascadeDeleteEnabled, ISet<object> transientEntities, CancellationToken cancellationToken);
4141
}

src/NHibernate/Async/Id/Insert/AbstractSelectingDelegate.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public async Task<object> PerformInsertAsync(SqlCommandInfo insertSql, ISessionI
5454
}
5555

5656
var selectSql = SelectSQL;
57-
using (new SessionIdLoggingContext(session.SessionId))
57+
using (session.BeginProcess())
5858
{
5959
try
6060
{
@@ -124,4 +124,4 @@ protected internal virtual Task BindParametersAsync(ISessionImplementor session,
124124
}
125125
}
126126
}
127-
}
127+
}

src/NHibernate/Async/Impl/AbstractSessionImpl.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public virtual async Task<IList> ListAsync(IQueryExpression queryExpression, Que
5858
public virtual async Task<IList<T>> ListAsync<T>(IQueryExpression query, QueryParameters parameters, CancellationToken cancellationToken)
5959
{
6060
cancellationToken.ThrowIfCancellationRequested();
61-
using (new SessionIdLoggingContext(SessionId))
61+
using (BeginProcess())
6262
{
6363
var results = new List<T>();
6464
await (ListAsync(query, parameters, results, cancellationToken)).ConfigureAwait(false);
@@ -69,7 +69,7 @@ public virtual async Task<IList<T>> ListAsync<T>(IQueryExpression query, QueryPa
6969
public virtual async Task<IList<T>> ListAsync<T>(CriteriaImpl criteria, CancellationToken cancellationToken)
7070
{
7171
cancellationToken.ThrowIfCancellationRequested();
72-
using (new SessionIdLoggingContext(SessionId))
72+
using (BeginProcess())
7373
{
7474
var results = new List<T>();
7575
await (ListAsync(criteria, results, cancellationToken)).ConfigureAwait(false);
@@ -82,7 +82,7 @@ public virtual async Task<IList<T>> ListAsync<T>(CriteriaImpl criteria, Cancella
8282
public virtual async Task<IList> ListAsync(CriteriaImpl criteria, CancellationToken cancellationToken)
8383
{
8484
cancellationToken.ThrowIfCancellationRequested();
85-
using (new SessionIdLoggingContext(SessionId))
85+
using (BeginProcess())
8686
{
8787
var results = new List<object>();
8888
await (ListAsync(criteria, results, cancellationToken)).ConfigureAwait(false);
@@ -113,7 +113,7 @@ public async Task<IList> ListFilterAsync(object collection, IQueryExpression que
113113
public virtual async Task<IList> ListAsync(NativeSQLQuerySpecification spec, QueryParameters queryParameters, CancellationToken cancellationToken)
114114
{
115115
cancellationToken.ThrowIfCancellationRequested();
116-
using (new SessionIdLoggingContext(SessionId))
116+
using (BeginProcess())
117117
{
118118
var results = new List<object>();
119119
await (ListAsync(spec, queryParameters, results, cancellationToken)).ConfigureAwait(false);
@@ -124,7 +124,7 @@ public virtual async Task<IList> ListAsync(NativeSQLQuerySpecification spec, Que
124124
public virtual async Task ListAsync(NativeSQLQuerySpecification spec, QueryParameters queryParameters, IList results, CancellationToken cancellationToken)
125125
{
126126
cancellationToken.ThrowIfCancellationRequested();
127-
using (new SessionIdLoggingContext(SessionId))
127+
using (BeginProcess())
128128
{
129129
var query = new SQLCustomQuery(
130130
spec.SqlQueryReturns,
@@ -138,7 +138,7 @@ public virtual async Task ListAsync(NativeSQLQuerySpecification spec, QueryParam
138138
public virtual async Task<IList<T>> ListAsync<T>(NativeSQLQuerySpecification spec, QueryParameters queryParameters, CancellationToken cancellationToken)
139139
{
140140
cancellationToken.ThrowIfCancellationRequested();
141-
using (new SessionIdLoggingContext(SessionId))
141+
using (BeginProcess())
142142
{
143143
var results = new List<T>();
144144
await (ListAsync(spec, queryParameters, results, cancellationToken)).ConfigureAwait(false);
@@ -151,7 +151,7 @@ public virtual async Task<IList<T>> ListAsync<T>(NativeSQLQuerySpecification spe
151151
public virtual async Task<IList<T>> ListCustomQueryAsync<T>(ICustomQuery customQuery, QueryParameters queryParameters, CancellationToken cancellationToken)
152152
{
153153
cancellationToken.ThrowIfCancellationRequested();
154-
using (new SessionIdLoggingContext(SessionId))
154+
using (BeginProcess())
155155
{
156156
var results = new List<T>();
157157
await (ListCustomQueryAsync(customQuery, queryParameters, results, cancellationToken)).ConfigureAwait(false);
@@ -170,7 +170,7 @@ public virtual async Task<IList<T>> ListCustomQueryAsync<T>(ICustomQuery customQ
170170
protected async Task AfterOperationAsync(bool success, CancellationToken cancellationToken)
171171
{
172172
cancellationToken.ThrowIfCancellationRequested();
173-
using (new SessionIdLoggingContext(SessionId))
173+
using (BeginContext())
174174
{
175175
if (!ConnectionManager.IsInActiveTransaction)
176176
{

src/NHibernate/Async/Impl/MultiCriteriaImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public partial class MultiCriteriaImpl : IMultiCriteria
3333
public async Task<IList> ListAsync(CancellationToken cancellationToken = default(CancellationToken))
3434
{
3535
cancellationToken.ThrowIfCancellationRequested();
36-
using (new SessionIdLoggingContext(session.SessionId))
36+
using (session.BeginProcess())
3737
{
3838
bool cacheable = session.Factory.Settings.IsQueryCacheEnabled && isCacheable;
3939

src/NHibernate/Async/Impl/MultiQueryImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public partial class MultiQueryImpl : IMultiQuery
3939
public async Task<IList> ListAsync(CancellationToken cancellationToken = default(CancellationToken))
4040
{
4141
cancellationToken.ThrowIfCancellationRequested();
42-
using (new SessionIdLoggingContext(session.SessionId))
42+
using (session.BeginProcess())
4343
{
4444
bool cacheable = session.Factory.Settings.IsQueryCacheEnabled && isCacheable;
4545
combinedParameters = CreateCombinedQueryParameters();

0 commit comments

Comments
 (0)