Beispiel #1
0
        protected virtual void Dispose(bool disposing)
        {
            if (_state.TryChange(State.Disposed))
            {
#pragma warning disable CS0618 // Type or member is obsolete
                var connectionModeSwitch = _description.ConnectionModeSwitch;
                var connectionMode       = connectionModeSwitch == ConnectionModeSwitch.UseConnectionMode ? _description.ConnectionMode : default;
                var directConnection     = connectionModeSwitch == ConnectionModeSwitch.UseDirectConnection ? _description.DirectConnection : default;

                var newClusterDescription = new ClusterDescription(
                    _clusterId,
                    connectionMode,
                    connectionModeSwitch,
                    directConnection,
                    dnsMonitorException: null,
                    ClusterType.Unknown,
                    Enumerable.Empty <ServerDescription>());
#pragma warning restore CS0618 // Type or member is obsolete

                UpdateClusterDescription(newClusterDescription);

                _rapidHeartbeatTimer.Dispose();
            }
        }
Beispiel #2
0
        private async Task WaitForDescriptionChangedAsync(IServerSelector selector, ClusterDescription description, Task descriptionChangedTask, TimeSpan timeout, CancellationToken cancellationToken)
        {
            var cancellationTaskCompletionSource = new TaskCompletionSource <bool>();

            using (cancellationToken.Register(() => cancellationTaskCompletionSource.TrySetCanceled()))
                using (var timeoutCancellationTokenSource = new CancellationTokenSource())
                {
                    var timeoutTask   = Task.Delay(timeout, timeoutCancellationTokenSource.Token);
                    var completedTask = await Task.WhenAny(descriptionChangedTask, timeoutTask, cancellationTaskCompletionSource.Task).ConfigureAwait(false);

                    if (completedTask == timeoutTask)
                    {
                        ThrowTimeoutException(selector, description);
                    }
                    timeoutCancellationTokenSource.Cancel();

                    if (completedTask == cancellationTaskCompletionSource.Task)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    await descriptionChangedTask.ConfigureAwait(false); // propagate exceptions
                }
        }
Beispiel #3
0
 public WaitForDescriptionChangedHelper(Cluster cluster, IServerSelector selector, ClusterDescription description, Task descriptionChangedTask, TimeSpan timeout, CancellationToken cancellationToken)
 {
     _cluster                          = cluster;
     _description                      = description;
     _selector                         = selector;
     _descriptionChangedTask           = descriptionChangedTask;
     _cancellationToken                = cancellationToken;
     _cancellationTaskCompletionSource = new TaskCompletionSource <bool>();
     _cancellationTokenRegistration    = cancellationToken.Register(() => _cancellationTaskCompletionSource.TrySetCanceled());
     _timeoutCancellationTokenSource   = new CancellationTokenSource();
     _timeoutTask                      = Task.Delay(timeout, _timeoutCancellationTokenSource.Token);
 }
Beispiel #4
0
        private void ThrowTimeoutException(IServerSelector selector, ClusterDescription description)
        {
            var message = BuildTimeoutExceptionMessage(_settings.ServerSelectionTimeout, selector, description);

            throw new TimeoutException(message);
        }
Beispiel #5
0
        private string BuildTimeoutExceptionMessage(TimeSpan timeout, IServerSelector selector, ClusterDescription clusterDescription)
        {
            var ms = (int)Math.Round(timeout.TotalMilliseconds);

            return(string.Format(
                       "A timeout occured after {0}ms selecting a server using {1}. Client view of cluster state is {2}.",
                       ms.ToString(),
                       selector.ToString(),
                       clusterDescription.ToString()));
        }
 // constructors
 /// <summary>
 /// Initializes a new instance of the <see cref="ClusterDescriptionChangedEventArgs"/> class.
 /// </summary>
 /// <param name="oldClusterDescription">The old cluster description.</param>
 /// <param name="newClusterDescription">The new cluster description.</param>
 public ClusterDescriptionChangedEventArgs(ClusterDescription oldClusterDescription, ClusterDescription newClusterDescription)
 {
     _oldClusterDescription = Ensure.IsNotNull(oldClusterDescription, "oldClusterDescription");
     _newClusterDescription = Ensure.IsNotNull(newClusterDescription, "newClusterDescription");
 }
Beispiel #7
0
        public async Task <IServer> SelectServerAsync(IServerSelector selector, CancellationToken cancellationToken)
        {
            ThrowIfDisposedOrNotOpen();
            Ensure.IsNotNull(selector, "selector");

            var timeoutAt = DateTime.UtcNow + _settings.ServerSelectionTimeout;

            var serverSelectionWaitQueueEntered = false;

            if (_settings.PreServerSelector != null || _settings.PostServerSelector != null)
            {
                var allSelectors = new List <IServerSelector>();
                if (_settings.PreServerSelector != null)
                {
                    allSelectors.Add(_settings.PreServerSelector);
                }

                allSelectors.Add(selector);

                if (_settings.PostServerSelector != null)
                {
                    allSelectors.Add(_settings.PostServerSelector);
                }

                selector = new CompositeServerSelector(allSelectors);
            }

            ClusterDescription description = null;

            try
            {
                var stopwatch = Stopwatch.StartNew();
                while (true)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    Task descriptionChangedTask;
                    lock (_descriptionLock)
                    {
                        descriptionChangedTask = _descriptionChangedTaskCompletionSource.Task;
                        description            = _description;
                    }

                    if (!serverSelectionWaitQueueEntered && _selectingServerEventHandler != null)
                    {
                        // this is our first time through...
                        _selectingServerEventHandler(new ClusterSelectingServerEvent(
                                                         description,
                                                         selector));
                    }

                    ThrowIfIncompatible(description);

                    var connectedServers = description.Servers.Where(s => s.State == ServerState.Connected);
                    var selectedServers  = selector.SelectServers(description, connectedServers).ToList();

                    while (selectedServers.Count > 0)
                    {
                        var server = selectedServers.Count == 1 ?
                                     selectedServers[0] :
                                     __randomServerSelector.SelectServers(description, selectedServers).Single();

                        IClusterableServer selectedServer;
                        if (TryGetServer(server.EndPoint, out selectedServer))
                        {
                            stopwatch.Stop();
                            if (_selectedServerEventHandler != null)
                            {
                                _selectedServerEventHandler(new ClusterSelectedServerEvent(
                                                                description,
                                                                selector,
                                                                server,
                                                                stopwatch.Elapsed));
                            }
                            return(selectedServer);
                        }

                        selectedServers.Remove(server);
                    }

                    if (!serverSelectionWaitQueueEntered)
                    {
                        EnterServerSelectionWaitQueue();
                        serverSelectionWaitQueueEntered = true;
                    }

                    var timeoutRemaining = timeoutAt - DateTime.UtcNow;
                    if (timeoutRemaining <= TimeSpan.Zero)
                    {
                        ThrowTimeoutException(selector, description);
                    }

                    await WaitForDescriptionChangedAsync(selector, description, descriptionChangedTask, timeoutRemaining, cancellationToken).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                if (_selectingServerFailedEventHandler != null)
                {
                    _selectingServerFailedEventHandler(new ClusterSelectingServerFailedEvent(
                                                           description,
                                                           selector,
                                                           ex));
                }
                throw;
            }
            finally
            {
                if (serverSelectionWaitQueueEntered)
                {
                    ExitServerSelectionWaitQueue();
                }
            }
        }
Beispiel #8
0
        private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDescription, ServerDescriptionChangedEventArgs args, List <IClusterableServer> newServers)
        {
            if (!args.NewServerDescription.Type.IsReplicaSetMember())
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, string.Format("Server is a {0}, not a replica set member.", args.NewServerDescription.Type)));
            }

            if (args.NewServerDescription.Type == ServerType.ReplicaSetGhost)
            {
                return(clusterDescription.WithServerDescription(args.NewServerDescription));
            }

            if (_replicaSetName == null)
            {
                _replicaSetName = args.NewServerDescription.ReplicaSetConfig.Name;
            }

            if (_replicaSetName != args.NewServerDescription.ReplicaSetConfig.Name)
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, string.Format("Server was a member of the '{0}' replica set, but should be '{1}'.", args.NewServerDescription.ReplicaSetConfig.Name, _replicaSetName)));
            }

            clusterDescription = clusterDescription.WithServerDescription(args.NewServerDescription);
            clusterDescription = EnsureServers(clusterDescription, args.NewServerDescription, newServers);

            if (args.NewServerDescription.CanonicalEndPoint != null &&
                !EndPointHelper.Equals(args.NewServerDescription.CanonicalEndPoint, args.NewServerDescription.EndPoint))
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, "CanonicalEndPoint is different than seed list EndPoint."));
            }

            if (args.NewServerDescription.Type == ServerType.ReplicaSetPrimary)
            {
                if (args.NewServerDescription.ReplicaSetConfig.Version != null)
                {
                    bool isCurrentPrimaryStale = true;
                    if (_maxElectionInfo != null)
                    {
                        isCurrentPrimaryStale = _maxElectionInfo.IsStale(args.NewServerDescription.ReplicaSetConfig.Version.Value, args.NewServerDescription.ElectionId);
                        var isReportedPrimaryStale = _maxElectionInfo.IsFresher(
                            args.NewServerDescription.ReplicaSetConfig.Version.Value,
                            args.NewServerDescription.ElectionId);


                        if (isReportedPrimaryStale && args.NewServerDescription.ElectionId != null)
                        {
                            // we only invalidate the "newly" reported stale primary if electionId was used.
                            lock (_serversLock)
                            {
                                var server = _servers.SingleOrDefault(x => EndPointHelper.Equals(args.NewServerDescription.EndPoint, x.EndPoint));
                                server.Invalidate();

                                _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() =>
                                                                                              string.Format(
                                                                                                  @"Invalidating server: Setting ServerType to ""Unknown"" for {0} because it " +
                                                                                                  @"claimed to be the replica set primary for replica set ""{1}"" but sent a " +
                                                                                                  @"(setVersion, electionId) tuple of ({2}, {3}) that was less than than the " +
                                                                                                  @"largest tuple seen, (maxSetVersion, maxElectionId), of ({4}, {5}).",
                                                                                                  args.NewServerDescription.EndPoint,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Name,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Version,
                                                                                                  args.NewServerDescription.ElectionId,
                                                                                                  _maxElectionInfo.SetVersion,
                                                                                                  _maxElectionInfo.ElectionId)));

                                return(clusterDescription.WithServerDescription(
                                           new ServerDescription(server.ServerId, server.EndPoint)));
                            }
                        }
                    }

                    if (isCurrentPrimaryStale)
                    {
                        if (_maxElectionInfo == null)
                        {
                            _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() =>
                                                                                          string.Format(
                                                                                              @"Initializing (maxSetVersion, maxElectionId): Saving tuple " +
                                                                                              @"(setVersion, electionId) of ({0}, {1}) as (maxSetVersion, maxElectionId) for " +
                                                                                              @"replica set ""{2}"" because replica set primary {3} sent ({0}, {1}), the first " +
                                                                                              @"(setVersion, electionId) tuple ever seen for replica set ""{4}"".",
                                                                                              args.NewServerDescription.ReplicaSetConfig.Version,
                                                                                              args.NewServerDescription.ElectionId,
                                                                                              args.NewServerDescription.ReplicaSetConfig.Name,
                                                                                              args.NewServerDescription.EndPoint,
                                                                                              args.NewServerDescription.ReplicaSetConfig.Name)));
                        }
                        else
                        {
                            if (_maxElectionInfo.SetVersion < args.NewServerDescription.ReplicaSetConfig.Version.Value)
                            {
                                _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() =>
                                                                                              string.Format(
                                                                                                  @"Updating stale setVersion: Updating the current " +
                                                                                                  @"(maxSetVersion, maxElectionId) tuple from ({0}, {1}) to ({2}, {3}) for " +
                                                                                                  @"replica set ""{4}"" because replica set primary {5} sent ({6}, {7})—a larger " +
                                                                                                  @"(setVersion, electionId) tuple then the saved tuple, ({0}, {1}).",
                                                                                                  _maxElectionInfo.SetVersion,
                                                                                                  _maxElectionInfo.ElectionId,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Version,
                                                                                                  args.NewServerDescription.ElectionId,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Name,
                                                                                                  args.NewServerDescription.EndPoint,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Version,
                                                                                                  args.NewServerDescription.ElectionId)));
                            }
                            else // current primary is stale & setVersion is not stale ⇒ the electionId must be stale
                            {
                                _sdamInformationEventHandler?.Invoke(new SdamInformationEvent(() =>
                                                                                              string.Format(
                                                                                                  @"Updating stale electionId: Updating the current " +
                                                                                                  @"(maxSetVersion, maxElectionId) tuple from ({0}, {1}) to ({2}, {3}) for " +
                                                                                                  @"replica set ""{4}"" because replica set primary {5} sent ({6}, {7})—" +
                                                                                                  @"a larger (setVersion, electionId) tuple than the saved tuple, ({0}, {1}).",
                                                                                                  _maxElectionInfo.SetVersion,
                                                                                                  _maxElectionInfo.ElectionId,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Version,
                                                                                                  args.NewServerDescription.ElectionId,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Name,
                                                                                                  args.NewServerDescription.EndPoint,
                                                                                                  args.NewServerDescription.ReplicaSetConfig.Version,
                                                                                                  args.NewServerDescription.ElectionId)));
                            }
                        }

                        _maxElectionInfo = new ElectionInfo(
                            args.NewServerDescription.ReplicaSetConfig.Version.Value,
                            args.NewServerDescription.ElectionId);
                    }
                }

                var currentPrimaryEndPoints = clusterDescription.Servers
                                              .Where(x => x.Type == ServerType.ReplicaSetPrimary)
                                              .Where(x => !EndPointHelper.Equals(x.EndPoint, args.NewServerDescription.EndPoint))
                                              .Select(x => x.EndPoint)
                                              .ToList();

                if (currentPrimaryEndPoints.Count > 0)
                {
                    lock (_serversLock)
                    {
                        var currentPrimaries = _servers.Where(x => EndPointHelper.Contains(currentPrimaryEndPoints, x.EndPoint));
                        foreach (var currentPrimary in currentPrimaries)
                        {
                            // kick off the server to invalidate itself
                            currentPrimary.Invalidate();
                            // set it to disconnected in the cluster
                            clusterDescription = clusterDescription.WithServerDescription(
                                new ServerDescription(currentPrimary.ServerId, currentPrimary.EndPoint));
                        }
                    }
                }
            }

            return(clusterDescription);
        }
Beispiel #9
0
        private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDescription, ServerDescriptionChangedEventArgs args, List <IClusterableServer> newServers)
        {
            if (!args.NewServerDescription.Type.IsReplicaSetMember())
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, string.Format("Server is a {0}, not a replica set member.", args.NewServerDescription.Type)));
            }

            if (args.NewServerDescription.Type == ServerType.ReplicaSetGhost)
            {
                return(clusterDescription.WithServerDescription(args.NewServerDescription));
            }

            if (_replicaSetName == null)
            {
                _replicaSetName = args.NewServerDescription.ReplicaSetConfig.Name;
            }

            if (_replicaSetName != args.NewServerDescription.ReplicaSetConfig.Name)
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, string.Format("Server was a member of the '{0}' replica set, but should be '{1}'.", args.NewServerDescription.ReplicaSetConfig.Name, _replicaSetName)));
            }

            clusterDescription = clusterDescription.WithServerDescription(args.NewServerDescription);
            clusterDescription = EnsureServers(clusterDescription, args.NewServerDescription, newServers);

            if (args.NewServerDescription.CanonicalEndPoint != null &&
                !EndPointHelper.Equals(args.NewServerDescription.CanonicalEndPoint, args.NewServerDescription.EndPoint))
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, "CanonicalEndPoint is different than seed list EndPoint."));
            }

            if (args.NewServerDescription.Type == ServerType.ReplicaSetPrimary)
            {
                if (args.NewServerDescription.ReplicaSetConfig.Version != null)
                {
                    bool isCurrentPrimaryStale = true;
                    if (_maxElectionInfo != null)
                    {
                        isCurrentPrimaryStale = _maxElectionInfo.IsStale(args.NewServerDescription.ReplicaSetConfig.Version.Value, args.NewServerDescription.ElectionId);
                        var isReportedPrimaryStale = !isCurrentPrimaryStale;

                        if (isReportedPrimaryStale && args.NewServerDescription.ElectionId != null)
                        {
                            // we only invalidate the "newly" reported stale primary if electionId was used.
                            lock (_serversLock)
                            {
                                var server = _servers.SingleOrDefault(x => EndPointHelper.Equals(args.NewServerDescription.EndPoint, x.EndPoint));
                                server.Invalidate();
                                return(clusterDescription.WithServerDescription(
                                           new ServerDescription(server.ServerId, server.EndPoint)));
                            }
                        }
                    }

                    if (isCurrentPrimaryStale)
                    {
                        _maxElectionInfo = new ElectionInfo(
                            args.NewServerDescription.ReplicaSetConfig.Version.Value,
                            args.NewServerDescription.ElectionId);
                    }
                }

                var currentPrimaryEndPoints = clusterDescription.Servers
                                              .Where(x => x.Type == ServerType.ReplicaSetPrimary)
                                              .Where(x => !EndPointHelper.Equals(x.EndPoint, args.NewServerDescription.EndPoint))
                                              .Select(x => x.EndPoint)
                                              .ToList();

                if (currentPrimaryEndPoints.Count > 0)
                {
                    lock (_serversLock)
                    {
                        var currentPrimaries = _servers.Where(x => EndPointHelper.Contains(currentPrimaryEndPoints, x.EndPoint));
                        foreach (var currentPrimary in currentPrimaries)
                        {
                            // kick off the server to invalidate itself
                            currentPrimary.Invalidate();
                            // set it to disconnected in the cluster
                            clusterDescription = clusterDescription.WithServerDescription(
                                new ServerDescription(currentPrimary.ServerId, currentPrimary.EndPoint));
                        }
                    }
                }
            }

            return(clusterDescription);
        }
Beispiel #10
0
        private ClusterDescription ProcessReplicaSetChange(ClusterDescription clusterDescription, ServerDescriptionChangedEventArgs args)
        {
            if (!args.NewServerDescription.Type.IsReplicaSetMember())
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, string.Format("Server is a {0}, not a replica set member.", args.NewServerDescription.Type)));
            }

            if (args.NewServerDescription.Type == ServerType.ReplicaSetGhost)
            {
                return(clusterDescription.WithServerDescription(args.NewServerDescription));
            }

            if (_replicaSetName == null)
            {
                _replicaSetName = args.NewServerDescription.ReplicaSetConfig.Name;
            }

            if (_replicaSetName != args.NewServerDescription.ReplicaSetConfig.Name)
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, string.Format("Server was a member of the '{0}' replica set, but should be '{1}'.", args.NewServerDescription.ReplicaSetConfig.Name, _replicaSetName)));
            }

            clusterDescription = clusterDescription.WithServerDescription(args.NewServerDescription);
            clusterDescription = EnsureServers(clusterDescription, args.NewServerDescription);

            if (args.NewServerDescription.CanonicalEndPoint != null &&
                !EndPointHelper.Equals(args.NewServerDescription.CanonicalEndPoint, args.NewServerDescription.EndPoint))
            {
                return(RemoveServer(clusterDescription, args.NewServerDescription.EndPoint, "CanonicalEndPoint is different than seed list EndPoint."));
            }

            if (args.NewServerDescription.Type == ServerType.ReplicaSetPrimary)
            {
                if (args.NewServerDescription.ElectionId != null)
                {
                    if (_maxElectionId != null && _maxElectionId.CompareTo(args.NewServerDescription.ElectionId) > 0)
                    {
                        // ignore this change because we've already seen this election id
                        lock (_serversLock)
                        {
                            var server = _servers.SingleOrDefault(x => EndPointHelper.Equals(args.NewServerDescription.EndPoint, x.EndPoint));
                            server.Invalidate();
                            return(clusterDescription.WithServerDescription(
                                       new ServerDescription(server.ServerId, server.EndPoint)));
                        }
                    }

                    _maxElectionId = args.NewServerDescription.ElectionId;
                }

                var currentPrimaryEndPoints = clusterDescription.Servers
                                              .Where(x => x.Type == ServerType.ReplicaSetPrimary)
                                              .Where(x => !EndPointHelper.Equals(x.EndPoint, args.NewServerDescription.EndPoint))
                                              .Select(x => x.EndPoint)
                                              .ToList();

                if (currentPrimaryEndPoints.Count > 0)
                {
                    lock (_serversLock)
                    {
                        var currentPrimaries = _servers.Where(x => EndPointHelper.Contains(currentPrimaryEndPoints, x.EndPoint));
                        foreach (var currentPrimary in currentPrimaries)
                        {
                            // kick off the server to invalidate itself
                            currentPrimary.Invalidate();
                            // set it to disconnected in the cluster
                            clusterDescription = clusterDescription.WithServerDescription(
                                new ServerDescription(currentPrimary.ServerId, currentPrimary.EndPoint));
                        }
                    }
                }
            }

            return(clusterDescription);
        }
Beispiel #11
0
            public IServer SelectServer()
            {
                lock (_cluster._descriptionLock)
                {
                    _descriptionChangedTask = _cluster._descriptionChangedTaskCompletionSource.Task;
                    _description            = _cluster._description;
                }

                if (!_serverSelectionWaitQueueEntered)
                {
                    var selectingServerEventHandler = _cluster._selectingServerEventHandler;
                    if (selectingServerEventHandler != null)
                    {
                        // this is our first time through...
                        selectingServerEventHandler(new ClusterSelectingServerEvent(
                                                        _description,
                                                        _selector,
                                                        EventContext.OperationId));
                    }
                }

                MongoIncompatibleDriverException.ThrowIfNotSupported(_description);

                _connectedServers.Clear();
                _connectedServerDescriptions.Clear();

                foreach (var description in _description.Servers)
                {
                    if (description.State == ServerState.Connected &&
                        _cluster.TryGetServer(description.EndPoint, out var server))
                    {
                        _connectedServers.Add(server);
                        _connectedServerDescriptions.Add(description);
                    }
                }

                var selectedServersDescriptions = _selector
                                                  .SelectServers(_description, _connectedServerDescriptions)
                                                  .ToList();

                IServer selectedServer = null;

                if (selectedServersDescriptions.Count > 0)
                {
                    var selectedServerDescription = selectedServersDescriptions.Count == 1
                        ? selectedServersDescriptions[0]
                        : __randomServerSelector.SelectServers(_description, selectedServersDescriptions).Single();

                    selectedServer = _connectedServers.FirstOrDefault(s => EndPointHelper.Equals(s.EndPoint, selectedServerDescription.EndPoint));
                }

                if (selectedServer != null)
                {
                    _stopwatch.Stop();

                    _cluster._selectedServerEventHandler?.Invoke(new ClusterSelectedServerEvent(
                                                                     _description,
                                                                     _selector,
                                                                     selectedServer.Description,
                                                                     _stopwatch.Elapsed,
                                                                     EventContext.OperationId));
                }

                return(selectedServer);
            }