-
Notifications
You must be signed in to change notification settings - Fork 2k
/
ControlledFaultInjectionTransactionTestRunner.cs
106 lines (97 loc) · 4.92 KB
/
ControlledFaultInjectionTransactionTestRunner.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
namespace Orleans.Transactions.TestKit
{
public class ControlledFaultInjectionTransactionTestRunner : TransactionTestRunnerBase
{
public ControlledFaultInjectionTransactionTestRunner(IGrainFactory grainFactory, Action<string> output)
: base(grainFactory, output)
{ }
public virtual async Task SingleGrainReadTransaction()
{
const int expected = 5;
IFaultInjectionTransactionTestGrain grain = grainFactory.GetGrain<IFaultInjectionTransactionTestGrain>(Guid.NewGuid());
await grain.Set(expected);
int actual = await grain.Get();
actual.Should().Be(expected);
await grain.Deactivate();
actual = await grain.Get();
actual.Should().Be(expected);
}
public virtual async Task SingleGrainWriteTransaction()
{
const int delta = 5;
IFaultInjectionTransactionTestGrain grain = this.grainFactory.GetGrain<IFaultInjectionTransactionTestGrain>(Guid.NewGuid());
int original = await grain.Get();
await grain.Add(delta);
await grain.Deactivate();
int expected = original + delta;
int actual = await grain.Get();
actual.Should().Be(expected);
}
public virtual async Task MultiGrainWriteTransaction_FaultInjection(TransactionFaultInjectPhase injectionPhase, FaultInjectionType injectionType)
{
const int setval = 5;
const int addval = 7;
int expected = setval + addval;
const int grainCount = TransactionTestConstants.MaxCoordinatedTransactions;
var faultInjectionControl = new FaultInjectionControl() { FaultInjectionPhase = injectionPhase, FaultInjectionType = injectionType };
List<IFaultInjectionTransactionTestGrain> grains =
Enumerable.Range(0, grainCount)
.Select(i => this.grainFactory.GetGrain<IFaultInjectionTransactionTestGrain>(Guid.NewGuid()))
.ToList();
IFaultInjectionTransactionCoordinatorGrain coordinator = this.grainFactory.GetGrain<IFaultInjectionTransactionCoordinatorGrain>(Guid.NewGuid());
await coordinator.MultiGrainSet(grains, setval);
// add delay between transactions so confirmation errors don't bleed into neighboring transactions
if (injectionPhase == TransactionFaultInjectPhase.BeforeConfirm || injectionPhase == TransactionFaultInjectPhase.AfterConfirm)
await Task.Delay(TimeSpan.FromSeconds(30));
try
{
await coordinator.MultiGrainAddAndFaultInjection(grains, addval, faultInjectionControl);
// add delay between transactions so confirmation errors don't bleed into neighboring transactions
if (injectionPhase == TransactionFaultInjectPhase.BeforeConfirm || injectionPhase == TransactionFaultInjectPhase.AfterConfirm)
await Task.Delay(TimeSpan.FromSeconds(30));
}
catch (OrleansTransactionAbortedException)
{
// add delay between transactions so errors don't bleed into neighboring transactions
await coordinator.MultiGrainAddAndFaultInjection(grains, addval);
}
catch (OrleansTransactionException e)
{
this.testOutput($"Call failed with exception: {e}, retrying without fault");
bool cascadingAbort = false;
bool firstAttempt = true;
do
{
cascadingAbort = false;
try
{
expected = await grains.First().Get() + addval;
await coordinator.MultiGrainAddAndFaultInjection(grains, addval);
}
catch (OrleansCascadingAbortException)
{
this.testOutput($"Retry failed with OrleansCascadingAbortException: {e}, retrying without fault");
// should only encounter this when faulting after storage write
injectionType.Should().Be(FaultInjectionType.ExceptionAfterStore);
// only allow one retry
firstAttempt.Should().BeTrue();
// add delay prevent castcading abort.
cascadingAbort = true;
firstAttempt = false;
}
} while (cascadingAbort);
}
//if transactional state loaded correctly after reactivation, then following should pass
foreach (var grain in grains)
{
int actual = await grain.Get();
actual.Should().Be(expected);
}
}
}
}