Skip to content

Commit 61c1b70

Browse files
committed
CSHARP-2992: Add LastHeartbeatTimestamp and ReasonChanged properties to ServerDescription.
1 parent 75ee36d commit 61c1b70

File tree

10 files changed

+67
-23
lines changed

10 files changed

+67
-23
lines changed

src/MongoDB.Driver.Core/Core/Clusters/MultiServerCluster.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes
380380
lock (_serversLock)
381381
{
382382
var server = _servers.SingleOrDefault(x => EndPointHelper.Equals(args.NewServerDescription.EndPoint, x.EndPoint));
383-
server.Invalidate();
383+
server.Invalidate("ReportedPrimaryIsStale");
384384

385385
_sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() =>
386386
string.Format(
@@ -396,7 +396,7 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes
396396
_maxElectionInfo.ElectionId)));
397397

398398
return clusterDescription.WithServerDescription(
399-
new ServerDescription(server.ServerId, server.EndPoint));
399+
new ServerDescription(server.ServerId, server.EndPoint, "ReportedPrimaryIsStale"));
400400
}
401401
}
402402
}
@@ -475,10 +475,10 @@ private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDes
475475
foreach (var currentPrimary in currentPrimaries)
476476
{
477477
// kick off the server to invalidate itself
478-
currentPrimary.Invalidate();
478+
currentPrimary.Invalidate("NoLongerPrimary");
479479
// set it to disconnected in the cluster
480480
clusterDescription = clusterDescription.WithServerDescription(
481-
new ServerDescription(currentPrimary.ServerId, currentPrimary.EndPoint));
481+
new ServerDescription(currentPrimary.ServerId, currentPrimary.EndPoint, "NoLongerPrimary"));
482482
}
483483
}
484484
}

src/MongoDB.Driver.Core/Core/Servers/IServer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ public interface IClusterableServer : IServer, IDisposable
102102
/// <summary>
103103
/// Invalidates this instance (sets the server type to Unknown and clears the connection pool).
104104
/// </summary>
105-
void Invalidate();
105+
/// <param name="reasonInvalidated">The reason the instance was invalidated.</param>
106+
void Invalidate(string reasonInvalidated);
106107

107108
/// <summary>
108109
/// Requests a heartbeat as soon as possible.

src/MongoDB.Driver.Core/Core/Servers/IServerMonitor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ internal interface IServerMonitor : IDisposable
3838
/// <summary>
3939
/// Instructs the monitor to refresh its description immediately.
4040
/// </summary>
41-
void Invalidate();
41+
/// <param name="reasonInvalidated">The reason the server was invalidated.</param>
42+
void Invalidate(string reasonInvalidated);
4243

4344
/// <summary>
4445
/// Requests a heartbeat as soon as possible.

src/MongoDB.Driver.Core/Core/Servers/Server.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,10 @@ public void Initialize()
221221
}
222222
}
223223

224-
public void Invalidate()
224+
public void Invalidate(string reasonInvalidated)
225225
{
226226
ThrowIfNotOpen();
227-
Invalidate(clearConnectionPool: true);
227+
Invalidate(reasonInvalidated, clearConnectionPool: true);
228228
}
229229

230230
public void RequestHeartbeat()
@@ -279,21 +279,21 @@ private void HandleChannelException(IConnection connection, Exception ex)
279279
if (ShouldInvalidateServer(ex))
280280
{
281281
var shouldClearConnectionPool = ShouldClearConnectionPoolForChannelException(ex, connection.Description.ServerVersion);
282-
Invalidate(shouldClearConnectionPool);
282+
Invalidate($"ChannelException:{ex}", shouldClearConnectionPool);
283283
}
284284
else
285285
{
286286
RequestHeartbeat();
287287
}
288288
}
289289

290-
private void Invalidate(bool clearConnectionPool)
290+
private void Invalidate(string reasonInvalidated, bool clearConnectionPool)
291291
{
292292
if (clearConnectionPool)
293293
{
294294
_connectionPool.Clear();
295295
}
296-
_monitor.Invalidate();
296+
_monitor.Invalidate(reasonInvalidated);
297297
}
298298

299299
private bool IsNotMaster(ServerErrorCode code, string message)

src/MongoDB.Driver.Core/Core/Servers/ServerDescription.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ public sealed class ServerDescription : IEquatable<ServerDescription>
3434
private readonly EndPoint _endPoint;
3535
private readonly Exception _heartbeatException;
3636
private readonly TimeSpan _heartbeatInterval;
37+
private readonly DateTime? _lastHeartbeatTimestamp;
3738
private readonly DateTime _lastUpdateTimestamp;
3839
private readonly DateTime? _lastWriteTimestamp;
3940
private readonly TimeSpan? _logicalSessionTimeout;
4041
private readonly int _maxBatchCount;
4142
private readonly int _maxDocumentSize;
4243
private readonly int _maxMessageSize;
4344
private readonly int _maxWireDocumentSize;
45+
private readonly string _reasonChanged;
4446
private readonly ReplicaSetConfig _replicaSetConfig;
4547
private readonly ServerId _serverId;
4648
private readonly ServerState _state;
@@ -55,11 +57,13 @@ public sealed class ServerDescription : IEquatable<ServerDescription>
5557
/// </summary>
5658
/// <param name="serverId">The server identifier.</param>
5759
/// <param name="endPoint">The end point.</param>
60+
/// <param name="reasonChanged">The reason the server description was last changed.</param>
5861
/// <param name="averageRoundTripTime">The average round trip time.</param>
5962
/// <param name="canonicalEndPoint">The canonical end point.</param>
6063
/// <param name="electionId">The election identifier.</param>
6164
/// <param name="heartbeatException">The heartbeat exception.</param>
6265
/// <param name="heartbeatInterval">The heartbeat interval.</param>
66+
/// <param name="lastHeartbeatTimestamp">The last heartbeat timestamp.</param>
6367
/// <param name="lastUpdateTimestamp">The last update timestamp.</param>
6468
/// <param name="lastWriteTimestamp">The last write timestamp.</param>
6569
/// <param name="logicalSessionTimeout">The logical session timeout.</param>
@@ -77,11 +81,13 @@ public sealed class ServerDescription : IEquatable<ServerDescription>
7781
public ServerDescription(
7882
ServerId serverId,
7983
EndPoint endPoint,
84+
Optional<string> reasonChanged = default(Optional<string>),
8085
Optional<TimeSpan> averageRoundTripTime = default(Optional<TimeSpan>),
8186
Optional<EndPoint> canonicalEndPoint = default(Optional<EndPoint>),
8287
Optional<ElectionId> electionId = default(Optional<ElectionId>),
8388
Optional<Exception> heartbeatException = default(Optional<Exception>),
8489
Optional<TimeSpan> heartbeatInterval = default(Optional<TimeSpan>),
90+
Optional<DateTime?> lastHeartbeatTimestamp = default(Optional<DateTime?>),
8591
Optional<DateTime> lastUpdateTimestamp = default(Optional<DateTime>),
8692
Optional<DateTime?> lastWriteTimestamp = default(Optional<DateTime?>),
8793
Optional<TimeSpan?> logicalSessionTimeout = default(Optional<TimeSpan?>),
@@ -109,13 +115,15 @@ public ServerDescription(
109115
_endPoint = endPoint;
110116
_heartbeatException = heartbeatException.WithDefault(null);
111117
_heartbeatInterval = heartbeatInterval.WithDefault(TimeSpan.Zero);
118+
_lastHeartbeatTimestamp = lastHeartbeatTimestamp.WithDefault(null);
112119
_lastUpdateTimestamp = lastUpdateTimestamp.WithDefault(DateTime.UtcNow);
113120
_lastWriteTimestamp = lastWriteTimestamp.WithDefault(null);
114121
_logicalSessionTimeout = logicalSessionTimeout.WithDefault(null);
115122
_maxBatchCount = maxBatchCount.WithDefault(1000);
116123
_maxDocumentSize = maxDocumentSize.WithDefault(4 * 1024 * 1024);
117124
_maxMessageSize = maxMessageSize.WithDefault(Math.Max(_maxDocumentSize + 1024, 16000000));
118125
_maxWireDocumentSize = maxWireDocumentSize.WithDefault(_maxDocumentSize + 16 * 1024);
126+
_reasonChanged = reasonChanged.WithDefault("NotSpecified");
119127
_replicaSetConfig = replicaSetConfig.WithDefault(null);
120128
_serverId = serverId;
121129
_state = state.WithDefault(ServerState.Disconnected);
@@ -223,6 +231,17 @@ public bool IsDataBearing
223231
}
224232
}
225233

234+
/// <summary>
235+
/// Gets the last heartbeat timestamp.
236+
/// </summary>
237+
/// <value>
238+
/// The last heartbeat timestamp.
239+
/// </value>
240+
public DateTime? LastHeartbeatTimestamp
241+
{
242+
get { return _lastHeartbeatTimestamp; }
243+
}
244+
226245
/// <summary>
227246
/// Gets the last update timestamp (when the ServerDescription itself was last updated).
228247
/// </summary>
@@ -300,6 +319,12 @@ public int MaxWireDocumentSize
300319
get { return _maxWireDocumentSize; }
301320
}
302321

322+
/// <summary>
323+
/// The reason the server description was last changed.
324+
/// </summary>
325+
/// <value>The reason the server description was last changed.</value>
326+
public string ReasonChanged => _reasonChanged;
327+
303328
/// <summary>
304329
/// Gets the replica set configuration.
305330
/// </summary>
@@ -399,13 +424,15 @@ public bool Equals(ServerDescription other)
399424
EndPointHelper.Equals(_endPoint, other._endPoint) &&
400425
object.Equals(_heartbeatException, other._heartbeatException) &&
401426
_heartbeatInterval == other._heartbeatInterval &&
427+
_lastHeartbeatTimestamp == other.LastHeartbeatTimestamp &&
402428
_lastUpdateTimestamp == other._lastUpdateTimestamp &&
403429
_lastWriteTimestamp == other._lastWriteTimestamp &&
404430
_logicalSessionTimeout == other._logicalSessionTimeout &&
405431
_maxBatchCount == other._maxBatchCount &&
406432
_maxDocumentSize == other._maxDocumentSize &&
407433
_maxMessageSize == other._maxMessageSize &&
408434
_maxWireDocumentSize == other._maxWireDocumentSize &&
435+
_reasonChanged.Equals(other._reasonChanged, StringComparison.Ordinal) &&
409436
object.Equals(_replicaSetConfig, other._replicaSetConfig) &&
410437
_serverId.Equals(other._serverId) &&
411438
_state == other._state &&
@@ -426,13 +453,15 @@ public override int GetHashCode()
426453
.Hash(_endPoint)
427454
.Hash(_heartbeatException)
428455
.Hash(_heartbeatInterval)
456+
.Hash(_lastHeartbeatTimestamp)
429457
.Hash(_lastUpdateTimestamp)
430458
.Hash(_lastWriteTimestamp)
431459
.Hash(_logicalSessionTimeout)
432460
.Hash(_maxBatchCount)
433461
.Hash(_maxDocumentSize)
434462
.Hash(_maxMessageSize)
435463
.Hash(_maxWireDocumentSize)
464+
.Hash(_reasonChanged)
436465
.Hash(_replicaSetConfig)
437466
.Hash(_serverId)
438467
.Hash(_state)
@@ -450,12 +479,14 @@ public override string ToString()
450479
.Append("{ ")
451480
.AppendFormat("ServerId: \"{0}\"", _serverId)
452481
.AppendFormat(", EndPoint: \"{0}\"", _endPoint)
482+
.AppendFormat(", ReasonChanged: \"{0}\"", _reasonChanged)
453483
.AppendFormat(", State: \"{0}\"", _state)
454484
.AppendFormat(", Type: \"{0}\"", _type)
455485
.AppendFormatIf(_tags != null && !_tags.IsEmpty, ", Tags: \"{0}\"", _tags)
456486
.AppendFormatIf(_state == ServerState.Connected, ", WireVersionRange: \"{0}\"", _wireVersionRange)
457487
.AppendFormatIf(_electionId != null, ", ElectionId: \"{0}\"", _electionId)
458488
.AppendFormatIf(_heartbeatException != null, ", HeartbeatException: \"{0}\"", _heartbeatException)
489+
.AppendFormat(", LastHeartbeatTimestamp: {0}", _lastHeartbeatTimestamp.HasValue ? "\"" + LastHeartbeatTimestamp.Value.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK") + "\"" : "null")
459490
.AppendFormat(", LastUpdateTimestamp: \"{0:yyyy-MM-ddTHH:mm:ss.fffffffK}\"", _lastUpdateTimestamp)
460491
.Append(" }")
461492
.ToString();
@@ -464,11 +495,13 @@ public override string ToString()
464495
/// <summary>
465496
/// Returns a new instance of ServerDescription with some values changed.
466497
/// </summary>
498+
/// <param name="reasonChanged">The reason the server description changed.</param>
467499
/// <param name="averageRoundTripTime">The average round trip time.</param>
468500
/// <param name="canonicalEndPoint">The canonical end point.</param>
469501
/// <param name="electionId">The election identifier.</param>
470502
/// <param name="heartbeatException">The heartbeat exception.</param>
471503
/// <param name="heartbeatInterval">The heartbeat interval.</param>
504+
/// <param name="lastHeartbeatTimestamp">The last heartbeat timestamp.</param>
472505
/// <param name="lastUpdateTimestamp">The last update timestamp.</param>
473506
/// <param name="lastWriteTimestamp">The last write timestamp.</param>
474507
/// <param name="logicalSessionTimeout">The logical session timeout.</param>
@@ -486,11 +519,13 @@ public override string ToString()
486519
/// A new instance of ServerDescription.
487520
/// </returns>
488521
public ServerDescription With(
522+
Optional<string> reasonChanged = default(Optional<string>),
489523
Optional<TimeSpan> averageRoundTripTime = default(Optional<TimeSpan>),
490524
Optional<EndPoint> canonicalEndPoint = default(Optional<EndPoint>),
491525
Optional<ElectionId> electionId = default(Optional<ElectionId>),
492526
Optional<Exception> heartbeatException = default(Optional<Exception>),
493527
Optional<TimeSpan> heartbeatInterval = default(Optional<TimeSpan>),
528+
Optional<DateTime?> lastHeartbeatTimestamp = default(Optional<DateTime?>),
494529
Optional<DateTime> lastUpdateTimestamp = default(Optional<DateTime>),
495530
Optional<DateTime?> lastWriteTimestamp = default(Optional<DateTime?>),
496531
Optional<TimeSpan?> logicalSessionTimeout = default(Optional<TimeSpan?>),
@@ -508,11 +543,13 @@ public ServerDescription With(
508543
return new ServerDescription(
509544
_serverId,
510545
_endPoint,
546+
reasonChanged,
511547
averageRoundTripTime: averageRoundTripTime.WithDefault(_averageRoundTripTime),
512548
canonicalEndPoint: canonicalEndPoint.WithDefault(_canonicalEndPoint),
513549
electionId: electionId.WithDefault(_electionId),
514550
heartbeatException: heartbeatException.WithDefault(_heartbeatException),
515551
heartbeatInterval: heartbeatInterval.WithDefault(_heartbeatInterval),
552+
lastHeartbeatTimestamp: lastHeartbeatTimestamp.WithDefault(_lastHeartbeatTimestamp),
516553
lastUpdateTimestamp: lastUpdateTimestamp.WithDefault(DateTime.UtcNow),
517554
lastWriteTimestamp: lastWriteTimestamp.WithDefault(_lastWriteTimestamp),
518555
logicalSessionTimeout: logicalSessionTimeout.WithDefault(_logicalSessionTimeout),
@@ -540,11 +577,13 @@ public ServerDescription WithHeartbeatException(Exception heartbeatException)
540577
return new ServerDescription(
541578
_serverId,
542579
_endPoint,
580+
reasonChanged: "HeartbeatFailed",
543581
averageRoundTripTime: _averageRoundTripTime,
544582
canonicalEndPoint: _canonicalEndPoint,
545583
electionId: _electionId,
546584
heartbeatException: heartbeatException,
547585
heartbeatInterval: _heartbeatInterval,
586+
lastHeartbeatTimestamp: DateTime.UtcNow,
548587
lastUpdateTimestamp: DateTime.UtcNow,
549588
lastWriteTimestamp: _lastWriteTimestamp,
550589
logicalSessionTimeout: _logicalSessionTimeout,

src/MongoDB.Driver.Core/Core/Servers/ServerMonitor.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public ServerMonitor(ServerId serverId, EndPoint endPoint, IConnectionFactory co
5858
_connectionFactory = Ensure.IsNotNull(connectionFactory, nameof(connectionFactory));
5959
Ensure.IsNotNull(eventSubscriber, nameof(eventSubscriber));
6060

61-
_baseDescription = _currentDescription = new ServerDescription(_serverId, endPoint, heartbeatInterval: heartbeatInterval);
61+
_baseDescription = _currentDescription = new ServerDescription(_serverId, endPoint, reasonChanged: "InitialDescription", heartbeatInterval: heartbeatInterval);
6262
_heartbeatInterval = heartbeatInterval;
6363
_timeout = timeout;
6464
_state = new InterlockedInt32(State.Initial);
@@ -91,9 +91,9 @@ public void Initialize()
9191
}
9292
}
9393

94-
public void Invalidate()
94+
public void Invalidate(string reasonInvalidated)
9595
{
96-
SetDescription(_baseDescription.With(lastUpdateTimestamp: DateTime.UtcNow));
96+
SetDescription(_baseDescription.With($"InvalidatedBecause:{reasonInvalidated}", lastUpdateTimestamp: DateTime.UtcNow));
9797
RequestHeartbeat();
9898
}
9999

@@ -240,6 +240,8 @@ private async Task<bool> HeartbeatAsync(CancellationToken cancellationToken)
240240
newDescription = newDescription.With(heartbeatException: heartbeatException);
241241
}
242242

243+
newDescription = newDescription.With(reasonChanged: "Heartbeat", lastHeartbeatTimestamp: DateTime.UtcNow);
244+
243245
SetDescription(newDescription);
244246

245247
return true;

tests/MongoDB.Driver.Core.TestHelpers/ServerDescriptionParser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public static ServerDescription Parse(BsonDocument args)
3737
return new ServerDescription(
3838
serverId,
3939
endPoint,
40+
"Test",
4041
logicalSessionTimeout: logicalSessionTimeout,
4142
state: state,
4243
type: serverType);

0 commit comments

Comments
 (0)