Skip to content

Commit bfbd264

Browse files
NH-4034 - flushing sessions sharing transaction on commit.
1 parent a4cbae9 commit bfbd264

12 files changed

+337
-55
lines changed

src/NHibernate.Test/DebugSessionFactory.cs

+29-16
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
44
using System.Data.Common;
5+
using System.Linq;
56
using System.Threading;
67
using log4net;
78
using NHibernate.Cache;
@@ -53,29 +54,41 @@ public bool CheckSessionsWereClosed()
5354
var allClosed = true;
5455
foreach (var session in _openedSessions)
5556
{
56-
if (session.IsOpen)
57+
// Do not inverse, we want to close all of them.
58+
allClosed = CheckSessionWasClosed(session) && allClosed;
59+
// Catches only session opened from another one while sharing the connection. Those
60+
// opened without sharing the connection stay un-monitored.
61+
foreach (var dependentSession in session.ConnectionManager.DependentSessions.ToList())
5762
{
58-
if (session.TransactionContext?.ShouldCloseSessionOnDistributedTransactionCompleted ?? false)
59-
{
60-
// Delayed transactions not having completed and closed their sessions? Give them a chance to complete.
61-
Thread.Sleep(100);
62-
if (!session.IsOpen)
63-
{
64-
_log.Warn($"Test case had a delayed close of session {session.SessionId}.");
65-
continue;
66-
}
67-
}
68-
69-
_log.Error($"Test case didn't close session {session.SessionId}, closing");
70-
allClosed = false;
71-
(session as ISession)?.Close();
72-
(session as IStatelessSession)?.Close();
63+
allClosed = CheckSessionWasClosed(dependentSession) && allClosed;
7364
}
7465
}
7566

7667
return allClosed;
7768
}
7869

70+
private bool CheckSessionWasClosed(ISessionImplementor session)
71+
{
72+
if (!session.IsOpen)
73+
return true;
74+
75+
if (session.TransactionContext?.ShouldCloseSessionOnDistributedTransactionCompleted ?? false)
76+
{
77+
// Delayed transactions not having completed and closed their sessions? Give them a chance to complete.
78+
Thread.Sleep(100);
79+
if (!session.IsOpen)
80+
{
81+
_log.Warn($"Test case had a delayed close of session {session.SessionId}.");
82+
return true;
83+
}
84+
}
85+
86+
_log.Error($"Test case didn't close session {session.SessionId}, closing");
87+
(session as ISession)?.Close();
88+
(session as IStatelessSession)?.Close();
89+
return false;
90+
}
91+
7992
ISessionBuilder ISessionFactory.WithOptions()
8093
{
8194
return new SessionBuilder(ActualFactory.WithOptions(), this);

src/NHibernate.Test/NHibernate.Test.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,8 @@
14641464
<Compile Include="TestDialects\PostgreSQL83TestDialect.cs" />
14651465
<Compile Include="TestDialects\SQLiteTestDialect.cs" />
14661466
<Compile Include="Tools\hbm2ddl\SchemaExportTests\ExportToFileFixture.cs" />
1467+
<Compile Include="TransactionTest\Person.cs" />
1468+
<Compile Include="TransactionTest\TransactionFixtureBase.cs" />
14671469
<Compile Include="TransformTests\ImplementationOfEqualityTests.cs" />
14681470
<Compile Include="TypesTest\CharClass.cs" />
14691471
<Compile Include="TypesTest\CharClassFixture.cs" />
@@ -3286,6 +3288,7 @@
32863288
<EmbeddedResource Include="NHSpecificTest\NH1291AnonExample\Mappings.hbm.xml" />
32873289
</ItemGroup>
32883290
<ItemGroup>
3291+
<EmbeddedResource Include="TransactionTest\Person.hbm.xml" />
32893292
<EmbeddedResource Include="NHSpecificTest\NH2241\Mappings.hbm.xml" />
32903293
<EmbeddedResource Include="Futures\Mappings.hbm.xml" />
32913294
<EmbeddedResource Include="SessionBuilder\Mappings.hbm.xml" />
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
1-
using System.Collections;
1+
using System.Collections.Generic;
2+
using System.Linq;
23
using System.Transactions;
3-
using NHibernate.DomainModel;
4+
using NHibernate.Linq;
5+
using NHibernate.Test.TransactionTest;
46
using NUnit.Framework;
57

68
namespace NHibernate.Test.SystemTransactions
79
{
810
[TestFixture]
9-
public class TransactionFixture : TestCase
11+
public class TransactionFixture : TransactionFixtureBase
1012
{
11-
protected override IList Mappings
12-
{
13-
get { return new string[] { "WZ.hbm.xml" }; }
14-
}
15-
1613
[Test]
1714
public void CanUseSystemTransactionsToCommit()
1815
{
19-
object identifier;
16+
int identifier;
2017
using(ISession session = Sfi.OpenSession())
2118
using(TransactionScope tx = new TransactionScope())
2219
{
23-
W s = new W();
20+
var s = new Person();
2421
session.Save(s);
2522
identifier = s.Id;
2623
tx.Complete();
@@ -29,11 +26,84 @@ public void CanUseSystemTransactionsToCommit()
2926
using (ISession session = Sfi.OpenSession())
3027
using (TransactionScope tx = new TransactionScope())
3128
{
32-
W w = session.Get<W>(identifier);
29+
var w = session.Get<Person>(identifier);
3330
Assert.IsNotNull(w);
3431
session.Delete(w);
3532
tx.Complete();
3633
}
3734
}
35+
36+
[Test]
37+
public void FlushFromTransactionAppliesToDisposedSharingSession()
38+
{
39+
var flushOrder = new List<int>();
40+
using (var s = OpenSession(new TestInterceptor(0, flushOrder)))
41+
{
42+
var builder = s.SessionWithOptions().Connection();
43+
44+
using (var t = new TransactionScope())
45+
{
46+
var p1 = new Person();
47+
var p2 = new Person();
48+
var p3 = new Person();
49+
var p4 = new Person();
50+
51+
using (var s1 = builder.Interceptor(new TestInterceptor(1, flushOrder)).OpenSession())
52+
s1.Save(p1);
53+
using (var s2 = builder.Interceptor(new TestInterceptor(2, flushOrder)).OpenSession())
54+
{
55+
s2.Save(p2);
56+
using (var s3 = s2.SessionWithOptions().Connection().Interceptor(new TestInterceptor(3, flushOrder)).OpenSession())
57+
s3.Save(p3);
58+
}
59+
s.Save(p4);
60+
t.Complete();
61+
}
62+
}
63+
64+
Assert.That(flushOrder, Is.EqualTo(new[] { 1, 2, 3, 0 }));
65+
66+
using (var s = OpenSession())
67+
using (var t = s.BeginTransaction())
68+
{
69+
Assert.That(s.Query<Person>().Count(), Is.EqualTo(4));
70+
t.Commit();
71+
}
72+
}
73+
74+
[Test]
75+
public void FlushFromTransactionAppliesToSharingSession()
76+
{
77+
var flushOrder = new List<int>();
78+
using (var s = OpenSession(new TestInterceptor(0, flushOrder)))
79+
{
80+
var builder = s.SessionWithOptions().Connection();
81+
82+
using (var s1 = builder.Interceptor(new TestInterceptor(1, flushOrder)).OpenSession())
83+
using (var s2 = builder.Interceptor(new TestInterceptor(2, flushOrder)).OpenSession())
84+
using (var s3 = s2.SessionWithOptions().Connection().Interceptor(new TestInterceptor(3, flushOrder)).OpenSession())
85+
using (var t = new TransactionScope())
86+
{
87+
var p1 = new Person();
88+
var p2 = new Person();
89+
var p3 = new Person();
90+
var p4 = new Person();
91+
s1.Save(p1);
92+
s2.Save(p2);
93+
s3.Save(p3);
94+
s.Save(p4);
95+
t.Complete();
96+
}
97+
}
98+
99+
Assert.That(flushOrder, Is.EqualTo(new[] { 1, 2, 3, 0 }));
100+
101+
using (var s = OpenSession())
102+
using (var t = s.BeginTransaction())
103+
{
104+
Assert.That(s.Query<Person>().Count(), Is.EqualTo(4));
105+
t.Commit();
106+
}
107+
}
38108
}
39109
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace NHibernate.Test.TransactionTest
4+
{
5+
public class Person
6+
{
7+
public virtual int Id { get; set; }
8+
9+
public virtual DateTime CreatedAt { get; set; } = DateTime.Now;
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3+
namespace="NHibernate.Test.TransactionTest"
4+
assembly="NHibernate.Test">
5+
6+
<class name="Person">
7+
<id name="Id">
8+
<generator class="hilo"/>
9+
</id>
10+
<property name="CreatedAt"/>
11+
</class>
12+
</hibernate-mapping>

src/NHibernate.Test/TransactionTest/TransactionFixture.cs

+43-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
using System;
22
using System.Collections;
3-
using System.Data.Common;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using NHibernate.Linq;
46
using NUnit.Framework;
57

68
namespace NHibernate.Test.TransactionTest
79
{
810
[TestFixture]
9-
public class TransactionFixture : TestCase
11+
public class TransactionFixture : TransactionFixtureBase
1012
{
11-
protected override IList Mappings
12-
{
13-
// The mapping is only actually needed in one test
14-
get { return new string[] {"Simple.hbm.xml"}; }
15-
}
16-
1713
[Test]
1814
public void SecondTransactionShouldntBeCommitted()
1915
{
@@ -74,25 +70,25 @@ public void CommandAfterTransactionShouldWork()
7470
{
7571
using (ISession s = OpenSession())
7672
{
77-
using (ITransaction t = s.BeginTransaction())
73+
using (s.BeginTransaction())
7874
{
7975
}
8076

81-
s.CreateQuery("from Simple").List();
77+
s.CreateQuery("from Person").List();
8278

8379
using (ITransaction t = s.BeginTransaction())
8480
{
8581
t.Commit();
8682
}
8783

88-
s.CreateQuery("from Simple").List();
84+
s.CreateQuery("from Person").List();
8985

9086
using (ITransaction t = s.BeginTransaction())
9187
{
9288
t.Rollback();
9389
}
9490

95-
s.CreateQuery("from Simple").List();
91+
s.CreateQuery("from Person").List();
9692
}
9793
}
9894

@@ -141,5 +137,40 @@ public void WasCommittedOrRolledBack()
141137
}
142138
}
143139
}
140+
141+
[Test]
142+
public void FlushFromTransactionAppliesToSharingSession()
143+
{
144+
var flushOrder = new List<int>();
145+
using (var s = OpenSession(new TestInterceptor(0, flushOrder)))
146+
{
147+
var builder = s.SessionWithOptions().Connection();
148+
149+
using (var s1 = builder.Interceptor(new TestInterceptor(1, flushOrder)).OpenSession())
150+
using (var s2 = builder.Interceptor(new TestInterceptor(2, flushOrder)).OpenSession())
151+
using (var s3 = s1.SessionWithOptions().Connection().Interceptor(new TestInterceptor(3, flushOrder)).OpenSession())
152+
using (var t = s.BeginTransaction())
153+
{
154+
var p1 = new Person();
155+
var p2 = new Person();
156+
var p3 = new Person();
157+
var p4 = new Person();
158+
s1.Save(p1);
159+
s2.Save(p2);
160+
s3.Save(p3);
161+
s.Save(p4);
162+
t.Commit();
163+
}
164+
}
165+
166+
Assert.That(flushOrder, Is.EqualTo(new[] { 1, 2, 3, 0 }));
167+
168+
using (var s = OpenSession())
169+
using (var t = s.BeginTransaction())
170+
{
171+
Assert.That(s.Query<Person>().Count(), Is.EqualTo(4));
172+
t.Commit();
173+
}
174+
}
144175
}
145176
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.TransactionTest
5+
{
6+
public abstract class TransactionFixtureBase : TestCase
7+
{
8+
protected override IList Mappings => new[] { "TransactionTest.Person.hbm.xml" };
9+
10+
protected override string MappingsAssembly => "NHibernate.Test";
11+
12+
protected override void OnTearDown()
13+
{
14+
using (var s = OpenSession())
15+
using (var t = s.BeginTransaction())
16+
{
17+
s.Delete("from System.Object");
18+
t.Commit();
19+
}
20+
}
21+
22+
public class TestInterceptor : EmptyInterceptor
23+
{
24+
private readonly int _numero;
25+
private readonly List<int> _flushOrder;
26+
27+
public TestInterceptor(int numero, List<int> flushOrder)
28+
{
29+
_numero = numero;
30+
_flushOrder = flushOrder;
31+
}
32+
33+
public override void PreFlush(ICollection entitites)
34+
{
35+
_flushOrder.Add(_numero);
36+
}
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)