Skip to content

Commit 0cc54a6

Browse files
committed
Merge branch '7.0' into 7.1
2 parents 1454097 + 3ebad0e commit 0cc54a6

File tree

5 files changed

+600
-398
lines changed

5 files changed

+600
-398
lines changed

Extensions/Xtensive.Orm.Reprocessing.Tests/ReprocessingBaseTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ namespace Xtensive.Orm.Reprocessing.Tests
1010
[TestFixture]
1111
public abstract class ReprocessingBaseTest : CommonModelTest
1212
{
13+
public const int DefaultTestTimeout = 8000;
14+
1315
protected override void CheckRequirements()
1416
{
1517
base.CheckRequirements();
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
// ReSharper disable ConvertToConstant.Local
2+
// ReSharper disable ReturnValueOfPureMethodIsNotUsed
3+
// ReSharper disable AccessToModifiedClosure
4+
5+
using System;
6+
using System.Linq;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using System.Transactions;
10+
using NUnit.Framework;
11+
using TestCommon.Model;
12+
13+
namespace Xtensive.Orm.Reprocessing.Tests.ReprocessingContext
14+
{
15+
public class Context
16+
{
17+
private readonly Domain domain;
18+
public int Count;
19+
private AutoResetEvent wait1 = new AutoResetEvent(false);
20+
private AutoResetEvent wait2 = new AutoResetEvent(false);
21+
22+
public void Deadlock(bool first, IsolationLevel? isolationLevel, TransactionOpenMode? transactionOpenMode)
23+
{
24+
TestContext.WriteLine("Context.DeadLock entered");
25+
domain.WithStrategy(ExecuteActionStrategy.HandleUniqueConstraintViolation).WithIsolationLevel(isolationLevel.GetValueOrDefault(IsolationLevel.RepeatableRead)).WithTransactionOpenMode(transactionOpenMode.GetValueOrDefault(TransactionOpenMode.New)).Execute(
26+
session => {
27+
_ = Interlocked.Increment(ref Count);
28+
_ = new Bar2(session, DateTime.Now, Guid.NewGuid()) { Name = Guid.NewGuid().ToString() };
29+
if (first) {
30+
_ = session.Query.All<Foo>().Lock(LockMode.Exclusive, LockBehavior.Wait).ToArray();
31+
if (wait1 != null) {
32+
_ = wait1.Set();
33+
_ = wait2.WaitOne();
34+
wait1 = null;
35+
}
36+
_ = session.Query.All<Bar>().Lock(LockMode.Exclusive, LockBehavior.Wait).ToArray();
37+
}
38+
else {
39+
_ = session.Query.All<Bar>().Lock(LockMode.Exclusive, LockBehavior.Wait).ToArray();
40+
if (wait2 != null) {
41+
_ = wait2.Set();
42+
_ = wait1.WaitOne();
43+
wait2 = null;
44+
}
45+
_ = session.Query.All<Foo>().Lock(LockMode.Exclusive, LockBehavior.Wait).ToArray();
46+
}
47+
});
48+
TestContext.WriteLine("Context.DeadLock left");
49+
}
50+
51+
public void External(
52+
bool first,
53+
IsolationLevel? isolationLevel,
54+
TransactionOpenMode? transactionOpenMode,
55+
Action<Session, bool, IsolationLevel?, TransactionOpenMode?> action)
56+
{
57+
TestContext.WriteLine("Context.External entered");
58+
using (var session = domain.OpenSession())
59+
using (var tran = isolationLevel == null ? null : session.OpenTransaction()) {
60+
if (tran != null) {
61+
session.EnsureTransactionIsStarted();
62+
_ = new Bar2(session, DateTime.Now, Guid.NewGuid()) { Name = Guid.NewGuid().ToString() };
63+
}
64+
if (first) {
65+
if (wait1 != null && wait2 != null) {
66+
_ = wait1.Set();
67+
_ = wait2.WaitOne();
68+
}
69+
}
70+
else if (wait1 != null && wait2 != null) {
71+
_ = wait2.Set();
72+
_ = wait1.WaitOne();
73+
}
74+
action(session, first, isolationLevel, transactionOpenMode);
75+
if (tran != null) {
76+
tran.Complete();
77+
}
78+
}
79+
TestContext.WriteLine("Context.External left");
80+
}
81+
82+
public void Parent(
83+
bool first,
84+
IsolationLevel? isolationLevel,
85+
TransactionOpenMode? transactionOpenMode,
86+
IExecuteActionStrategy strategy,
87+
Action<bool, IsolationLevel?, TransactionOpenMode?> action)
88+
{
89+
TestContext.WriteLine("Context.Parent1 entered");
90+
domain.WithStrategy(strategy)
91+
.WithIsolationLevel(isolationLevel.GetValueOrDefault(IsolationLevel.RepeatableRead))
92+
.WithTransactionOpenMode(transactionOpenMode.GetValueOrDefault(TransactionOpenMode.New))
93+
.Execute(
94+
session => {
95+
session.EnsureTransactionIsStarted();
96+
_ = new Bar2(session, DateTime.Now, Guid.NewGuid()) { Name = Guid.NewGuid().ToString() };
97+
if (first) {
98+
if (wait1 != null && wait2 != null) {
99+
_ = wait1.Set();
100+
_ = wait2.WaitOne();
101+
}
102+
}
103+
else if (wait1 != null && wait2 != null) {
104+
_ = wait2.Set();
105+
_ = wait1.WaitOne();
106+
}
107+
action(first, isolationLevel, transactionOpenMode);
108+
});
109+
TestContext.WriteLine("Context.Parent1 left");
110+
}
111+
112+
public void Parent(
113+
Session session,
114+
bool first,
115+
IsolationLevel? isolationLevel,
116+
TransactionOpenMode? transactionOpenMode,
117+
IExecuteActionStrategy strategy,
118+
Action<bool, IsolationLevel?, TransactionOpenMode?> action)
119+
{
120+
TestContext.WriteLine("Context.Parent2 entered");
121+
domain.WithStrategy(strategy)
122+
.WithSession(session)
123+
.WithIsolationLevel(isolationLevel.GetValueOrDefault(IsolationLevel.RepeatableRead))
124+
.WithTransactionOpenMode(transactionOpenMode.GetValueOrDefault(TransactionOpenMode.New))
125+
.Execute(
126+
session => {
127+
session.EnsureTransactionIsStarted();
128+
_ = new Bar2(session, DateTime.Now, Guid.NewGuid()) { Name = Guid.NewGuid().ToString() };
129+
if (first) {
130+
if (wait1 != null && wait2 != null) {
131+
_ = wait1.Set();
132+
_ = wait2.WaitOne();
133+
}
134+
}
135+
else if (wait1 != null && wait2 != null) {
136+
_ = wait2.Set();
137+
_ = wait1.WaitOne();
138+
}
139+
action(first, isolationLevel, transactionOpenMode);
140+
});
141+
TestContext.WriteLine("Context.Parent2 left");
142+
}
143+
144+
public void Run(
145+
IsolationLevel? isolationLevel,
146+
TransactionOpenMode? transactionOpenMode,
147+
Action<bool, IsolationLevel?, TransactionOpenMode?> action)
148+
{
149+
TestContext.WriteLine("Context.Run entered");
150+
domain.Execute(
151+
session => {
152+
session.Remove(session.Query.All<Foo>());
153+
session.Remove(session.Query.All<Bar>());
154+
session.Remove(session.Query.All<Bar2>());
155+
_ = new Bar(session);
156+
_ = new Foo(session);
157+
});
158+
TestContext.WriteLine("Context.Run executed Domain.Execute");
159+
TestContext.WriteLine("Context.Run Parallel.Invoke started");
160+
Parallel.Invoke(
161+
() => action(true, isolationLevel, transactionOpenMode),
162+
() => action(false, isolationLevel, transactionOpenMode));
163+
TestContext.WriteLine("Context.Run Parallel.Invoke ended");
164+
TestContext.WriteLine("Context.Run left");
165+
}
166+
167+
public void UniqueConstraintViolation(
168+
bool first, IsolationLevel? isolationLevel, TransactionOpenMode? transactionOpenMode)
169+
{
170+
TestContext.WriteLine("Context.UniqueConstraintViolation entered");
171+
domain.WithStrategy(ExecuteActionStrategy.HandleUniqueConstraintViolation).WithIsolationLevel(isolationLevel.GetValueOrDefault(IsolationLevel.RepeatableRead)).WithTransactionOpenMode(transactionOpenMode.GetValueOrDefault(TransactionOpenMode.New)).Execute(
172+
session => {
173+
_ = Interlocked.Increment(ref Count);
174+
session.EnsureTransactionIsStarted();
175+
_ = new Bar2(session, DateTime.Now, Guid.NewGuid()) { Name = Guid.NewGuid().ToString() };
176+
var name = "test";
177+
if (!session.Query.All<Foo>().Any(a => a.Name == name)) {
178+
if (first) {
179+
if (wait1 != null && wait2 != null) {
180+
_ = wait1.Set();
181+
_ = wait2.WaitOne();
182+
wait1 = null;
183+
}
184+
}
185+
else if (wait2 != null && wait2 != null) {
186+
_ = wait2.Set();
187+
_ = wait1.WaitOne();
188+
wait2 = null;
189+
}
190+
_ = new Foo(session) { Name = name };
191+
}
192+
session.SaveChanges();
193+
});
194+
TestContext.WriteLine("Context.UniqueConstraintViolation left");
195+
}
196+
197+
public void UniqueConstraintViolationPrimaryKey(
198+
bool first, IsolationLevel? isolationLevel, TransactionOpenMode? transactionOpenMode)
199+
{
200+
TestContext.WriteLine("Context.UniqueConstraintViolationPrimaryKey entered");
201+
domain.WithStrategy(ExecuteActionStrategy.HandleUniqueConstraintViolation).WithIsolationLevel(isolationLevel.GetValueOrDefault(IsolationLevel.RepeatableRead)).WithTransactionOpenMode(transactionOpenMode.GetValueOrDefault(TransactionOpenMode.New)).Execute(
202+
session => {
203+
_ = Interlocked.Increment(ref Count);
204+
session.EnsureTransactionIsStarted();
205+
_ = new Bar2(session, DateTime.Now, Guid.NewGuid()) { Name = Guid.NewGuid().ToString() };
206+
var id = 10;
207+
if (session.Query.SingleOrDefault<Foo>(id) == null) {
208+
var w1 = wait1;
209+
var w2 = wait2;
210+
if (first) {
211+
if (w1 != null && w2 != null) {
212+
_ = w1.Set();
213+
_ = w2.WaitOne();
214+
wait1 = null;
215+
}
216+
}
217+
else if (w1 != null && w2 != null) {
218+
_ = w2.Set();
219+
_ = w1.WaitOne();
220+
wait2 = null;
221+
}
222+
_ = new Foo(session, id) { Name = Guid.NewGuid().ToString() };
223+
}
224+
session.SaveChanges();
225+
});
226+
TestContext.WriteLine("Context.UniqueConstraintViolationPrimaryKey left");
227+
}
228+
229+
public Context(Domain domain)
230+
{
231+
this.domain = domain;
232+
}
233+
}
234+
}
235+
236+
// ReSharper restore ReturnValueOfPureMethodIsNotUsed
237+
// ReSharper restore ConvertToConstant.Local
238+
// ReSharper restore AccessToModifiedClosure
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// ReSharper disable ConvertToConstant.Local
2+
// ReSharper disable ReturnValueOfPureMethodIsNotUsed
3+
// ReSharper disable AccessToModifiedClosure
4+
5+
using System;
6+
using System.Linq;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
using System.Transactions;
10+
using NUnit.Framework;
11+
using TestCommon.Model;
12+
using Xtensive.Orm.Reprocessing.Tests.ReprocessingContext;
13+
14+
namespace Xtensive.Orm.Reprocessing.Tests
15+
{
16+
[TestFixture, Timeout(DefaultTestTimeout * 4)]
17+
public class DeadlockReprocessing : ReprocessingBaseTest
18+
{
19+
[Test, Timeout(DefaultTestTimeout)]
20+
public void SimpleDeadlockTest()
21+
{
22+
Console.WriteLine("Test started");
23+
24+
var context = new Context(Domain);
25+
context.Run(IsolationLevel.Serializable, null, context.Deadlock);
26+
Assert.That(context.Count, Is.EqualTo(3));
27+
Assert.That(Bar2Count(), Is.EqualTo(2));
28+
}
29+
30+
[Test, Timeout(DefaultTestTimeout)]
31+
public void NestedSerializableDeadlockTest()
32+
{
33+
Console.WriteLine("Test started");
34+
35+
var context = new Context(Domain);
36+
context.Run(
37+
IsolationLevel.Serializable,
38+
null,
39+
(b, level, open) => context.Parent(b, level, open, ExecuteActionStrategy.HandleReprocessableException, context.Deadlock));
40+
Assert.That(context.Count, Is.EqualTo(3));
41+
Assert.That(Bar2Count(), Is.EqualTo(4));
42+
}
43+
44+
[Test, Timeout(DefaultTestTimeout)]
45+
public void NestedSnapshotDeadlockTest()
46+
{
47+
Console.WriteLine("Test started");
48+
49+
var context = new Context(Domain);
50+
context.Run(
51+
IsolationLevel.Snapshot,
52+
null,
53+
(b, level, open) => context.Parent(b, level, open, ExecuteActionStrategy.HandleReprocessableException, context.Deadlock));
54+
Assert.That(context.Count, Is.EqualTo(3));
55+
Assert.That(Bar2Count(), Is.EqualTo(4));
56+
}
57+
58+
[Test, Timeout(DefaultTestTimeout)]
59+
public void NestedNestedSerializableSerializableTest()
60+
{
61+
Console.WriteLine("Test started");
62+
63+
//nested nested serializable deadlock
64+
var context = new Context(Domain);
65+
context.Run(
66+
IsolationLevel.Serializable,
67+
null,
68+
(b, level, open) =>
69+
context.Parent(
70+
b,
71+
level,
72+
open,
73+
ExecuteActionStrategy.HandleReprocessableException,
74+
(b1, level1, open1) =>
75+
context.Parent(b1, level1, open1, ExecuteActionStrategy.HandleReprocessableException, context.Deadlock)));
76+
Assert.That(context.Count, Is.EqualTo(3));
77+
Assert.That(Bar2Count(), Is.EqualTo(6));
78+
}
79+
80+
private int Bar2Count()
81+
{
82+
return Domain.Execute(session => session.Query.All<Bar2>().Count());
83+
}
84+
}
85+
}
86+
87+
// ReSharper restore ReturnValueOfPureMethodIsNotUsed
88+
// ReSharper restore ConvertToConstant.Local
89+
// ReSharper restore AccessToModifiedClosure

0 commit comments

Comments
 (0)