@@ -47,6 +47,7 @@ internal sealed class ServerMonitor : IServerMonitor
47
47
private readonly Action < ServerHeartbeatStartedEvent > _heartbeatStartedEventHandler ;
48
48
private readonly Action < ServerHeartbeatSucceededEvent > _heartbeatSucceededEventHandler ;
49
49
private readonly Action < ServerHeartbeatFailedEvent > _heartbeatFailedEventHandler ;
50
+ private readonly Action < SdamInformationEvent > _sdamInformationEventHandler ;
50
51
51
52
public event EventHandler < ServerDescriptionChangedEventArgs > DescriptionChanged ;
52
53
@@ -64,6 +65,7 @@ public ServerMonitor(ServerId serverId, EndPoint endPoint, IConnectionFactory co
64
65
eventSubscriber . TryGetEventHandler ( out _heartbeatStartedEventHandler ) ;
65
66
eventSubscriber . TryGetEventHandler ( out _heartbeatSucceededEventHandler ) ;
66
67
eventSubscriber . TryGetEventHandler ( out _heartbeatFailedEventHandler ) ;
68
+ eventSubscriber . TryGetEventHandler ( out _sdamInformationEventHandler ) ;
67
69
}
68
70
69
71
public ServerDescription Description => Interlocked . CompareExchange ( ref _currentDescription , null , null ) ;
@@ -91,7 +93,7 @@ public void Initialize()
91
93
92
94
public void Invalidate ( )
93
95
{
94
- OnDescriptionChanged ( _baseDescription ) ;
96
+ SetDescriptionIfChanged ( _baseDescription ) ;
95
97
RequestHeartbeat ( ) ;
96
98
}
97
99
@@ -113,7 +115,47 @@ private async Task MonitorServerAsync()
113
115
{
114
116
try
115
117
{
116
- await HeartbeatAsync ( heartbeatCancellationToken ) . ConfigureAwait ( false ) ;
118
+ try
119
+ {
120
+ await HeartbeatAsync ( heartbeatCancellationToken ) . ConfigureAwait ( false ) ;
121
+ }
122
+ catch ( OperationCanceledException ) when ( heartbeatCancellationToken . IsCancellationRequested )
123
+ {
124
+ // ignore OperationCanceledException when heartbeat cancellation is requested
125
+ }
126
+ catch ( Exception unexpectedException )
127
+ {
128
+ // if we catch an exception here it's because of a bug in the driver (but we need to defend ourselves against that)
129
+
130
+ var handler = _sdamInformationEventHandler ;
131
+ if ( handler != null )
132
+ {
133
+ try
134
+ {
135
+ handler . Invoke ( new SdamInformationEvent ( ( ) =>
136
+ string . Format (
137
+ "Unexpected exception in ServerMonitor.MonitorServerAsync: {0}" ,
138
+ unexpectedException . ToString ( ) ) ) ) ;
139
+ }
140
+ catch
141
+ {
142
+ // ignore any exceptions thrown by the handler (note: event handlers aren't supposed to throw exceptions)
143
+ }
144
+ }
145
+
146
+ // since an unexpected exception was thrown set the server description to Unknown (with the unexpected exception)
147
+ try
148
+ {
149
+ // keep this code as simple as possible to keep the surface area with any remaining possible bugs as small as possible
150
+ var newDescription = _baseDescription . WithHeartbeatException ( unexpectedException ) ; // not With in case the bug is in With
151
+ SetDescription ( newDescription ) ; // not SetDescriptionIfChanged in case the bug is in SetDescriptionIfChanged
152
+ }
153
+ catch
154
+ {
155
+ // if even the simple code in the try throws just give up (at least we've raised the unexpected exception via an SdamInformationEvent)
156
+ }
157
+ }
158
+
117
159
var newHeartbeatDelay = new HeartbeatDelay ( metronome . GetNextTickDelay ( ) , __minHeartbeatInterval ) ;
118
160
var oldHeartbeatDelay = Interlocked . Exchange ( ref _heartbeatDelay , newHeartbeatDelay ) ;
119
161
if ( oldHeartbeatDelay != null )
@@ -198,7 +240,7 @@ private async Task<bool> HeartbeatAsync(CancellationToken cancellationToken)
198
240
newDescription = newDescription . With ( heartbeatException : heartbeatException ) ;
199
241
}
200
242
201
- OnDescriptionChanged ( newDescription ) ;
243
+ SetDescriptionIfChanged ( newDescription ) ;
202
244
203
245
return true ;
204
246
}
@@ -257,15 +299,8 @@ private async Task<HeartbeatInfo> GetHeartbeatInfoAsync(IConnection connection,
257
299
}
258
300
}
259
301
260
- private void OnDescriptionChanged ( ServerDescription newDescription )
302
+ private void OnDescriptionChanged ( ServerDescription oldDescription , ServerDescription newDescription )
261
303
{
262
- var oldDescription = Interlocked . CompareExchange ( ref _currentDescription , null , null ) ;
263
- if ( oldDescription . Equals ( newDescription ) )
264
- {
265
- return ;
266
- }
267
- Interlocked . Exchange ( ref _currentDescription , newDescription ) ;
268
-
269
304
var handler = DescriptionChanged ;
270
305
if ( handler != null )
271
306
{
@@ -275,6 +310,29 @@ private void OnDescriptionChanged(ServerDescription newDescription)
275
310
}
276
311
}
277
312
313
+ private void SetDescription ( ServerDescription newDescription )
314
+ {
315
+ var oldDescription = Interlocked . CompareExchange ( ref _currentDescription , null , null ) ;
316
+ SetDescription ( oldDescription , newDescription ) ;
317
+ }
318
+
319
+ private void SetDescription ( ServerDescription oldDescription , ServerDescription newDescription )
320
+ {
321
+ Interlocked . Exchange ( ref _currentDescription , newDescription ) ;
322
+ OnDescriptionChanged ( oldDescription , newDescription ) ;
323
+ }
324
+
325
+ private void SetDescriptionIfChanged ( ServerDescription newDescription )
326
+ {
327
+ var oldDescription = Interlocked . CompareExchange ( ref _currentDescription , null , null ) ;
328
+ if ( oldDescription . Equals ( newDescription ) )
329
+ {
330
+ return ;
331
+ }
332
+
333
+ SetDescription ( oldDescription , newDescription ) ;
334
+ }
335
+
278
336
private void ThrowIfDisposed ( )
279
337
{
280
338
if ( _state . Value == State . Disposed )
0 commit comments