1
+ using System ;
2
+ using System . Linq ;
3
+ using System . Threading . Tasks ;
4
+ using Cleipnir . ResilientFunctions . CoreRuntime . Watchdogs ;
5
+ using Cleipnir . ResilientFunctions . Domain ;
6
+ using Cleipnir . ResilientFunctions . Helpers ;
7
+ using Cleipnir . ResilientFunctions . Storage ;
8
+ using Cleipnir . ResilientFunctions . Tests . Utils ;
9
+ using Shouldly ;
10
+
11
+ namespace Cleipnir . ResilientFunctions . Tests . TestTemplates . WatchDogsTests ;
12
+
13
+ public abstract class ReplicaWatchdogTests
14
+ {
15
+ public abstract Task SunshineScenario ( ) ;
16
+ public async Task SunshineScenario ( Task < IFunctionStore > storeTask )
17
+ {
18
+ var store = await storeTask . SelectAsync ( s => s . ReplicaStore ) ;
19
+ var replicaId1 = new ReplicaId ( Guid . Parse ( "10000000-0000-0000-0000-000000000000" ) ) ;
20
+ using var watchdog1 = new ReplicaWatchdog (
21
+ replicaId1 ,
22
+ store ,
23
+ checkFrequency : TimeSpan . FromHours ( 1 ) ,
24
+ onStrikeOut : _ => { }
25
+ ) ;
26
+ await watchdog1 . Initialize ( ) ;
27
+ var allReplicas = await store . GetAll ( ) ;
28
+ allReplicas . Count . ShouldBe ( 1 ) ;
29
+ var storedReplica1 = allReplicas . Single ( sr => sr . ReplicaId == replicaId1 . Id ) ;
30
+ storedReplica1 . Heartbeat . ShouldBe ( 0 ) ;
31
+
32
+ var replicaId2 = new ReplicaId ( Guid . Parse ( "20000000-0000-0000-0000-000000000000" ) ) ;
33
+ using var watchdog2 = new ReplicaWatchdog (
34
+ replicaId2 ,
35
+ store ,
36
+ checkFrequency : TimeSpan . FromHours ( 1 ) ,
37
+ onStrikeOut : _ => { }
38
+ ) ;
39
+ await watchdog2 . Initialize ( ) ;
40
+ allReplicas = await store . GetAll ( ) ;
41
+ allReplicas . Count . ShouldBe ( 2 ) ;
42
+ storedReplica1 = allReplicas . Single ( sr => sr . ReplicaId == replicaId1 . Id ) ;
43
+ storedReplica1 . Heartbeat . ShouldBe ( 0 ) ;
44
+ var storedReplica2 = allReplicas . Single ( sr => sr . ReplicaId == replicaId2 . Id ) ;
45
+ storedReplica2 . Heartbeat . ShouldBe ( 0 ) ;
46
+
47
+ await watchdog1 . PerformIteration ( ) ;
48
+ var replicas = await store . GetAll ( ) ;
49
+ replicas . Single ( sr => sr . ReplicaId == replicaId1 . Id ) . Heartbeat . ShouldBe ( 1 ) ;
50
+ replicas . Single ( sr => sr . ReplicaId == replicaId2 . Id ) . Heartbeat . ShouldBe ( 0 ) ;
51
+ watchdog1 . Strikes [ new StoredReplica ( replicaId2 . Id , Heartbeat : 0 ) ] . ShouldBe ( 0 ) ;
52
+ watchdog1 . Strikes [ new StoredReplica ( replicaId1 . Id , Heartbeat : 1 ) ] . ShouldBe ( 0 ) ;
53
+
54
+ await watchdog1 . PerformIteration ( ) ;
55
+ replicas = await store . GetAll ( ) ;
56
+ replicas . Single ( sr => sr . ReplicaId == replicaId1 . Id ) . Heartbeat . ShouldBe ( 2 ) ;
57
+ replicas . Single ( sr => sr . ReplicaId == replicaId2 . Id ) . Heartbeat . ShouldBe ( 0 ) ;
58
+ watchdog1 . Strikes [ new StoredReplica ( replicaId2 . Id , Heartbeat : 0 ) ] . ShouldBe ( 1 ) ;
59
+ watchdog1 . Strikes [ new StoredReplica ( replicaId1 . Id , Heartbeat : 2 ) ] . ShouldBe ( 0 ) ;
60
+
61
+ await watchdog1 . PerformIteration ( ) ;
62
+ replicas = await store . GetAll ( ) ;
63
+ replicas . Count . ShouldBe ( 1 ) ;
64
+ replicas . Single ( sr => sr . ReplicaId == replicaId1 . Id ) . Heartbeat . ShouldBe ( 3 ) ;
65
+ watchdog1 . Strikes . Count . ShouldBe ( 1 ) ;
66
+ watchdog1 . Strikes [ new StoredReplica ( replicaId1 . Id , Heartbeat : 3 ) ] . ShouldBe ( 0 ) ;
67
+ }
68
+
69
+ public abstract Task ReplicaWatchdogStartResultsInAddedReplicaInStore ( ) ;
70
+ public async Task ReplicaWatchdogStartResultsInAddedReplicaInStore ( Task < IFunctionStore > storeTask )
71
+ {
72
+ var store = await storeTask . SelectAsync ( s => s . ReplicaStore ) ;
73
+ var replicaId1 = new ReplicaId ( Guid . Parse ( "10000000-0000-0000-0000-000000000000" ) ) ;
74
+ using var watchdog1 = new ReplicaWatchdog (
75
+ replicaId1 ,
76
+ store ,
77
+ checkFrequency : TimeSpan . FromHours ( 1 ) ,
78
+ onStrikeOut : _ => { }
79
+ ) ;
80
+ await watchdog1 . Start ( ) ;
81
+ var allReplicas = await store . GetAll ( ) ;
82
+ allReplicas . Count . ShouldBe ( 1 ) ;
83
+
84
+ var replicaId2 = new ReplicaId ( Guid . Parse ( "20000000-0000-0000-0000-000000000000" ) ) ;
85
+ using var watchdog2 = new ReplicaWatchdog (
86
+ replicaId2 ,
87
+ store ,
88
+ checkFrequency : TimeSpan . FromHours ( 1 ) ,
89
+ onStrikeOut : _ => { }
90
+ ) ;
91
+ await watchdog2 . Start ( ) ;
92
+ allReplicas = await store . GetAll ( ) ;
93
+ allReplicas . Count . ShouldBe ( 2 ) ;
94
+ }
95
+
96
+ public abstract Task StrikedOutReplicaIsRemovedFromStore ( ) ;
97
+ public async Task StrikedOutReplicaIsRemovedFromStore ( Task < IFunctionStore > storeTask )
98
+ {
99
+ var store = await storeTask . SelectAsync ( s => s . ReplicaStore ) ;
100
+ var toBeStrikedOut = Guid . NewGuid ( ) ;
101
+ Guid ? strikedOut = null ;
102
+ await store . Insert ( toBeStrikedOut ) ;
103
+ var replicaId1 = new ReplicaId ( Guid . Parse ( "10000000-0000-0000-0000-000000000000" ) ) ;
104
+ using var watchdog1 = new ReplicaWatchdog (
105
+ replicaId1 ,
106
+ store ,
107
+ checkFrequency : TimeSpan . FromHours ( 1 ) ,
108
+ onStrikeOut : id => strikedOut = id
109
+ ) ;
110
+ await watchdog1 . Initialize ( ) ;
111
+ await watchdog1 . PerformIteration ( ) ;
112
+ strikedOut . ShouldBeNull ( ) ;
113
+ await watchdog1 . PerformIteration ( ) ;
114
+ strikedOut . ShouldBeNull ( ) ;
115
+ await watchdog1 . PerformIteration ( ) ;
116
+ strikedOut . ShouldBe ( toBeStrikedOut ) ;
117
+
118
+ var all = await store . GetAll ( ) ;
119
+ all . Count . ShouldBe ( 1 ) ;
120
+ all . Single ( ) . ReplicaId . ShouldBe ( replicaId1 . Id ) ;
121
+ }
122
+
123
+ public abstract Task RunningWatchdogUpdatesItsOwnHeartbeat ( ) ;
124
+ public async Task RunningWatchdogUpdatesItsOwnHeartbeat ( Task < IFunctionStore > storeTask )
125
+ {
126
+ var store = await storeTask . SelectAsync ( s => s . ReplicaStore ) ;
127
+ var anyStrikesOut = false ;
128
+ var replicaId1 = new ReplicaId ( Guid . NewGuid ( ) ) ;
129
+ using var watchdog1 = new ReplicaWatchdog (
130
+ replicaId1 ,
131
+ store ,
132
+ checkFrequency : TimeSpan . FromMilliseconds ( 100 ) ,
133
+ onStrikeOut : _ => anyStrikesOut = true
134
+ ) ;
135
+
136
+ await watchdog1 . Start ( ) ;
137
+
138
+ await BusyWait . Until ( async ( ) =>
139
+ {
140
+ var all = await store . GetAll ( ) ;
141
+ all . Count . ShouldBe ( 1 ) ;
142
+ var single = all . Single ( ) ;
143
+ single . ReplicaId . ShouldBe ( replicaId1 . Id ) ;
144
+ return single . Heartbeat > 0 ;
145
+ } ) ;
146
+
147
+ anyStrikesOut . ShouldBe ( false ) ;
148
+ }
149
+
150
+ public abstract Task ReplicaIdOffsetIfCalculatedCorrectly ( ) ;
151
+ public async Task ReplicaIdOffsetIfCalculatedCorrectly ( Task < IFunctionStore > storeTask )
152
+ {
153
+ var store = await storeTask . SelectAsync ( s => s . ReplicaStore ) ;
154
+
155
+ var replicaId1 = new ReplicaId ( Guid . Parse ( "10000000-0000-0000-0000-000000000000" ) ) ;
156
+ var replicaId2 = new ReplicaId ( Guid . Parse ( "20000000-0000-0000-0000-000000000000" ) ) ;
157
+ var replicaId3 = new ReplicaId ( Guid . Parse ( "30000000-0000-0000-0000-000000000000" ) ) ;
158
+
159
+ var watchdog1 = new ReplicaWatchdog ( replicaId1 , store , checkFrequency : TimeSpan . FromHours ( 1 ) , onStrikeOut : _ => { } ) ;
160
+ var watchdog2 = new ReplicaWatchdog ( replicaId2 , store , checkFrequency : TimeSpan . FromHours ( 1 ) , onStrikeOut : _ => { } ) ;
161
+ var watchdog3 = new ReplicaWatchdog ( replicaId3 , store , checkFrequency : TimeSpan . FromHours ( 1 ) , onStrikeOut : _ => { } ) ;
162
+
163
+ await watchdog1 . Initialize ( ) ;
164
+ await watchdog2 . Initialize ( ) ;
165
+ await watchdog3 . Initialize ( ) ;
166
+
167
+ await watchdog3 . PerformIteration ( ) ;
168
+ replicaId3 . Offset . ShouldBe ( 2 ) ;
169
+ await watchdog2 . PerformIteration ( ) ;
170
+ replicaId2 . Offset . ShouldBe ( 1 ) ;
171
+ await watchdog1 . PerformIteration ( ) ;
172
+ replicaId1 . Offset . ShouldBe ( 0 ) ;
173
+ }
174
+
175
+ public abstract Task NonExistingReplicaIdOffsetIsNull ( ) ;
176
+ public Task NonExistingReplicaIdOffsetIsNull ( Task < IFunctionStore > storeTask )
177
+ {
178
+ var offset = ReplicaWatchdog . CalculateOffset ( allReplicaIds : [ ] , ownReplicaId : Guid . NewGuid ( ) ) ;
179
+ offset . ShouldBeNull ( ) ;
180
+
181
+ return Task . CompletedTask ;
182
+ }
183
+ }
0 commit comments