public ElectionBasedMembershipStrategy(IShard shard, NodeContext context, LocalShardHeartbeatReporting heartbeatTable, ClusterConfigurationManager clusterConfigMgr)
 {
     this._shard            = shard;
     this._context          = context;
     this._clusterConfigMgr = clusterConfigMgr;
     Initialize();
 }
        /// <summary>
        /// Authoritative phase:
        /// 1. the requesting node acquires an election lock up at the config server.
        /// 2. It then proceeds with the election conduction where it asks for votes.
        /// 3. Each node checks their world view once more before voting with a yes or no.
        /// 4. Its mandatory for a consensus amongst all the nodes for an election term to be successful.
        /// 5. One negative vote nullifies the current election term. If such is the case, the entire procedure
        /// needs to be repeated.
        /// </summary>
        /// <param name="reportTable"></param>
        /// <returns></returns>
        private ElectionResult HoldElection(LocalShardHeartbeatReporting reportTable, Activity activity)
        {
            ElectionId electionId = null;

            ServerNode requestingNode = new ServerNode();

            requestingNode.Name     = _context.LocalAddress.IpAddress.ToString();
            requestingNode.Priority = (int)_manager.LocalServerPriority;
            ElectionType type = ElectionType.None;

            if (activity == Activity.GeneralElectionsTriggered)
            {
                type = ElectionType.GeneralElections;
            }
            else if (activity == Activity.TakeoverElectionsTriggered)
            {
                type = ElectionType.TakeoverElections;
            }

            Object response = _context.ConfigurationSession.BeginElection(_context.ClusterName.ToLower(), _context.LocalShardName.ToLower(), requestingNode, type);

            if (response != null)
            {
                electionId = response as ElectionId;
                if (electionId != null)
                {
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Info("ElectionBasedMembershipStrategy", type + " started for node " + _context.LocalAddress);
                    }
                    Address[] activeNodes = reportTable.GetReportTable.Keys.ToArray();

                    return(_manager.ConductElection(electionId, activeNodes, activity));
                }
                else
                {
                    //if we get a database exception from the CS, we wait for a configurable amount of time
                    //before trying to elect a new primary again.
                    if (response is DatabaseException && ((DatabaseException)response).ErrorCode == ErrorCodes.Cluster.PRIMARY_ALREADY_EXISTS)
                    {
                        lock (_mutexOnWait)
                        {
                            Monitor.Wait(_mutexOnWait, _waitTimeout);
                        }
                    }
                }
            }
            if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
            {
                LoggerManager.Instance.ShardLogger.Debug("ElectionBasedMembershipStrategy.HoldElection() ", "Node " + _context.LocalAddress.ToString() + " failed to acquire an election lock on the CS.");
            }
            return(null);
        }
        public object OnMessageReceived(object message, Server source, LocalShardHeartbeatReporting heartbeatReport, Membership existingMembership)
        {
            Election election = message as Election;

            if (election != null)
            {
                HeartbeatInfo localHeartbeat = heartbeatReport.GetHeartbeatInfo(_context.LocalAddress);
                if (localHeartbeat != null)
                {
                    return(_manager.CastVote(election, existingMembership, localHeartbeat));
                }
            }
            return(null);
        }
        public void TriggerElectionMechanism(Activity activity, Server server, LocalShardHeartbeatReporting heartbeatReport, Membership existingMembership)
        {
            LoggerManager.Instance.SetThreadContext(new LoggerContext()
            {
                ShardName = _context.LocalShardName != null ? _context.LocalShardName : "", DatabaseName = ""
            });
            if (existingMembership == null)
            {
                existingMembership = new Membership();
            }
            ShardConfiguration sConfig = null;

            //Get the shard configuration
            if (_clusterConfigMgr != null)
            {
                sConfig = _clusterConfigMgr.GetShardConfiguration(_context.LocalShardName);
            }
            IList <Address>      activeNodes       = null;
            MembershipChangeArgs args              = new MembershipChangeArgs();
            ServerNodes          staticServerNodes = null;

            if (sConfig == null || sConfig.Servers == null)
            {
                if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsWarnEnabled)
                {
                    LoggerManager.Instance.ShardLogger.Warn("ElectionBasedMembershipStrategy.TiggerElectionMechanism() ", "The shard " + _context.LocalShardName + " does not exist in the configuration.");
                }
                return;
            }
            staticServerNodes = sConfig.Servers;
            ElectionResult result = null;

            if (heartbeatReport != null)
            {
                activeNodes = heartbeatReport.GetReportTable.Keys.ToList();
            }

            Address activityNode = null;

            if (server == null)
            {
                activityNode = _context.LocalAddress;
            }
            else
            {
                activityNode = server.Address;
            }

            switch (activity)
            {
            case Activity.NodeJoining:
                if (server == null)
                {
                    return;
                }
                //On node join, we need to get membership from the config server for the first time.
                Membership csMembership = _context.ConfigurationSession.GetMembershipInfo(_context.ClusterName, _context.LocalShardName);
                ServerNode joiningNode  = sConfig.Servers.GetServerNode(server.Address.IpAddress.ToString());
                // If the added node is configured while the cluster is up and running, do the following.
                if (joiningNode == null)
                {
                    if (_clusterConfigMgr != null)
                    {
                        _clusterConfigMgr.UpdateClusterConfiguration();
                        sConfig = _clusterConfigMgr.GetShardConfiguration(_context.LocalShardName);
                    }
                    if (sConfig == null)
                    {
                        if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsWarnEnabled)
                        {
                            LoggerManager.Instance.ShardLogger.Warn("ElectionBasedMembershipStrategy.TriggerElectionMechanism() ", "The shard " + _context.LocalShardName + " does not exist in the configuration.");
                        }
                        return;
                    }
                    joiningNode = sConfig.Servers.GetServerNode(server.Address.IpAddress.ToString());
                }
                if (joiningNode == null)
                {
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsWarnEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Warn("ElectionBasedMembershipStrategy.TriggerElectionMechanism() ", "The node " + server.Address + " is not part of the configuration.");
                    }
                    return;
                }

                if (existingMembership == null || existingMembership.Servers == null || !existingMembership.Servers.Contains(joiningNode))
                {
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Debug("ElectBasedMemSt.TriggerElectMech", "Node joining activity triggered for " + activityNode);
                    }
                }
                bool        thisNodeIsPrimary = false;
                OperationId lastOpId          = null;

                if (heartbeatReport != null && heartbeatReport.GetReportTable.ContainsKey(server.Address))
                {
                    args.ServerName = _context.LocalAddress;
                    args.ElectionId = null;
                    args.ChangeType = MembershipChangeArgs.MembershipChangeType.NodeJoined;

                    if (server.Address.Equals(_context.LocalAddress))
                    {
                        _context.ConfigurationSession.ReportNodeJoining(_context.ClusterName, _context.LocalShardName, sConfig.Servers.GetServerNode(_context.LocalAddress.IpAddress.ToString()));

                        if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                        {
                            LoggerManager.Instance.ShardLogger.Info("electBasedMemSt.TriggerElectMech", server.Address + " reported its joining to the config server. ");
                        }

                        //if the primary is not null and the channel is not disconnected, it can be set here.
                        if ((existingMembership == null || existingMembership.Primary == null) && csMembership.Primary != null && _shard.ActiveChannelsList.Contains(new Server(new Address(csMembership.Primary.Name, sConfig.Port), Status.Initializing)) && ObeysMajorityRule(_shard.ActiveChannelsList.Count, sConfig.Servers.Nodes.Count))
                        {
                            args.ServerName = new Address(csMembership.Primary.Name, sConfig.Port);
                            args.ElectionId = csMembership.ElectionId;
                            args.ChangeType = MembershipChangeArgs.MembershipChangeType.PrimarySet;
                            //if the node which was lost comes back up before the CS or the nodes can declare it dead,
                            //it should resume its status as a primary. There should be no need for an election in this case.
                            if (args.ServerName.Equals(_context.LocalAddress))
                            {
                                thisNodeIsPrimary = true;
                            }
                        }
                    }

                    if (thisNodeIsPrimary)
                    {
                        if (csMembership.ElectionId != null && LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                        {
                            LoggerManager.Instance.ShardLogger.Info("electBasedMemSt.TriggerElectMech", "election_id: " + csMembership.ElectionId.Id + " election time :" + csMembership.ElectionId.ElectionTime);
                        }


                        if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                        {
                            LoggerManager.Instance.ShardLogger.Info("electBasedMemSt.TriggerElectMech", "I am already declared primary");
                        }
                        lastOpId = LastOperationId;
                        ChangeMembershipShardwide(args);
                    }
                    else
                    {
                        ((LocalShard)_shard).OnMembershipChanged(args);
                    }

                    if (server.Address.Equals(_context.LocalAddress))
                    {
                        ServerNode sNode = sConfig.Servers.GetServerNode(_context.LocalAddress.IpAddress.ToString());
                        if (sNode == null)
                        {
                            if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsWarnEnabled)
                            {
                                LoggerManager.Instance.ShardLogger.Warn("ElectionBasedMembershipStrategy.TriggerElectionMechanism() ", "The node " + sNode.Name + " does not exist in the configuration.");
                            }
                            return;
                        }

                        _context.ConfigurationSession.ReportHeartbeat(_context.ClusterName, _context.LocalShardName, sNode, existingMembership, lastOpId);
                    }
                }
                else
                {
                    if (existingMembership.Primary != null && existingMembership.Primary.Name.Equals(server.Address.IpAddress.ToString()))
                    {
                        if (sConfig.Servers == null || sConfig.Servers.Nodes == null || !ObeysMajorityRule(activeNodes.Count, sConfig.Servers.Nodes.Count))
                        {
                            _context.ConfigurationSession.SetNodeStatus(_context.ClusterName, _context.LocalShardName, existingMembership.Primary, NodeRole.None);
                            args.ChangeType = MembershipChangeArgs.MembershipChangeType.PrimaryDemoted;
                            args.ServerName = _context.LocalAddress;
                            ChangeMembershipShardwide(args);
                            if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                            {
                                LoggerManager.Instance.ShardLogger.Info("electBasedMemSt.TriggerElectMech", " Node addition activity occured. Primary node " + _context.LocalAddress.IpAddress.ToString() + " demoted.");
                            }
                            return;
                        }
                    }
                }

                break;

            case Activity.NodeLeaving:
                if (server == null)
                {
                    return;
                }
                bool hasMajority = ObeysMajorityRule(activeNodes.Count, staticServerNodes.Nodes.Count);
                args.ServerName = server.Address;
                args.ChangeType = MembershipChangeArgs.MembershipChangeType.NodeLeft;

                _clusterConfigMgr.UpdateClusterConfiguration();

                if (existingMembership.Primary != null)
                {
                    // if the existing primary is actually the node lost, we need to update the configuration.

                    if (existingMembership.Primary.Name == server.Address.IpAddress.ToString())
                    {
                        //if Primary leaves, it should be updated locally.
                        args.ChangeType = MembershipChangeArgs.MembershipChangeType.PrimaryLost;
                        if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                        {
                            LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.TriggerElectMech", "Node leaving activity triggered for " + server.Address + " . Primary lost.");
                        }
                    }
                    else if (existingMembership.Primary.Name == _context.LocalAddress.IpAddress.ToString())     // if the existing primary is the local node, we need to check for possible demotion of the current primary.
                    {
                        if (!hasMajority)
                        {
                            _context.ConfigurationSession.SetNodeStatus(_context.ClusterName, _context.LocalShardName, existingMembership.Primary, NodeRole.None);
                            args.ChangeType = MembershipChangeArgs.MembershipChangeType.PrimaryDemoted;
                            args.ServerName = _context.LocalAddress;
                            ChangeMembershipShardwide(args);

                            if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                            {
                                LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.TriggerElectMech", " Node leaving activity occurred. Primary node " + _context.LocalAddress.IpAddress.ToString() + " demoted.");
                            }

                            return;
                        }
                    }
                }

                ((LocalShard)_shard).OnMembershipChanged(args);

                break;

            case Activity.GeneralElectionsTriggered:
            case Activity.TakeoverElectionsTriggered:
                // this is where the actual election mechanism takes place.

                //Step 1: if no node in the heartbeat table has a primary and there is no primary in the local node's membership, we proceed forward.
                //Else if there is a primary but this looks like the takeover election mechanism, we proceed along as well.
                if ((activity.Equals(Activity.GeneralElectionsTriggered) && !heartbeatReport.PrimaryExists() && existingMembership.Primary == null) || (activity.Equals(Activity.TakeoverElectionsTriggered) && heartbeatReport.PrimaryExists()))
                {
                    //Step 2: we verify that this node has a majority of the shard nodes connected to it.
                    if (activeNodes != null && ObeysMajorityRule(activeNodes.Count, staticServerNodes.Nodes.Count))
                    {
                        //Step 3: Perform the initial sanity check. (Speculative phase)
                        if (ShouldIInitiateElection(heartbeatReport, activity))
                        {
                            if (existingMembership != null && existingMembership.Primary != null && activity == Activity.GeneralElectionsTriggered)
                            {
                                if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                                {
                                    LoggerManager.Instance.ShardLogger.Info("electBasedMemSt.TriggerElectMech", "A primary has already been selected for "
                                                                            + _context.LocalShardName + " hence exiting the election mechanism.");
                                }
                                return;
                            }
                            //Step 4: The elections take place in real. (Authoritative Phase)
                            result = HoldElection(heartbeatReport, activity);
                            if (result != null)
                            {
                                if (result.PollingResult == ElectionResult.Result.PrimarySelected)
                                {
                                    //if the shard is undergoing the takeover election mechanism, the old primary needs to
                                    //be demoted first.
                                    bool oldPrimaryDemoted = false;
                                    if (activity == Activity.TakeoverElectionsTriggered)
                                    {
                                        MembershipChangeArgs args2 = new MembershipChangeArgs();
                                        args2.ChangeType = MembershipChangeArgs.MembershipChangeType.ForcefullyDemotePrimary;
                                        args2.ServerName = _context.LocalAddress;
                                        args2.ElectionId = existingMembership.ElectionId;

                                        Message msg = new Message();
                                        msg.Payload       = args2;
                                        msg.MessageType   = MessageType.MembershipOperation;
                                        msg.NeedsResponse = true;
                                        ShardRequestBase <bool> request = _shard.CreateUnicastRequest <bool>(new Server(new Address(existingMembership.Primary.Name, sConfig.Port), Status.Running), msg);
                                        IAsyncResult            result2 = request.BeginExecute();
                                        oldPrimaryDemoted = request.EndExecute(result2);
                                    }
                                    //Submit the result to the CS.
                                    if (activity == Activity.GeneralElectionsTriggered || (activity == Activity.TakeoverElectionsTriggered && oldPrimaryDemoted))
                                    {
                                        _context.ConfigurationSession.SubmitElectionResult(_context.ClusterName.ToLower(), _context.LocalShardName.ToLower(), result);
                                    }
                                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                                    {
                                        LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.TriggerElectMech", "Election result submitted for shard " + _context.LocalShardName.ToString());
                                    }

                                    _context.ElectionResult = result;
                                    args.ServerName         = _context.LocalAddress;
                                    args.ElectionId         = result.ElectionId;
                                    args.ChangeType         = MembershipChangeArgs.MembershipChangeType.PrimarySelected;
                                    //Once, the result is submitted, inform the shard nodes.
                                    ChangeMembershipShardwide(args);
                                    _context.ConfigurationSession.ReportHeartbeat(_context.ClusterName, _context.LocalShardName, result.ElectedPrimary, existingMembership, LastOperationId);
                                }
                                //Finally, end this round of elections.
                                _context.ConfigurationSession.EndElection(_context.ClusterName, _context.LocalShardName, result.ElectionId);
                            }
                        }
                    }
                }

                break;

            case Activity.CSDisconnected:
                //this is called whenever a node loses connection with the config server.

                if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                {
                    LoggerManager.Instance.ShardLogger.Debug("ElectionBasedMembershipStrategy.TriggerElectionMechanism() ", "Config Server disconnected. ");
                }
                //if the number of configured nodes are even and the primary loses connection with the CS, it needs to demote itself.
                if (existingMembership != null && existingMembership.Primary != null && existingMembership.Primary.Name == _context.LocalAddress.IpAddress.ToString() && staticServerNodes.Nodes.Count % 2 == 0)
                {
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.TriggerElectMech", " Connection of the node " + _context.LocalAddress.ToString() + " with the config server is lost.");
                    }

                    args.ServerName = _context.LocalAddress;
                    args.ElectionId = existingMembership.ElectionId;
                    args.ChangeType = MembershipChangeArgs.MembershipChangeType.PrimaryDemoted;
                    ChangeMembershipShardwide(args);
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Info("electBasedMemSt.TriggerElectMech", " Primary node " + _context.LocalAddress.IpAddress.ToString() + " demoted because the primary lost connection with the CS.");
                    }
                }
                break;

            case Activity.ForcefulPrimaryDemotion:
                if (existingMembership != null && existingMembership.Primary != null && existingMembership.Primary.Name == _context.LocalAddress.IpAddress.ToString())
                {
                    _context.ConfigurationSession.SetNodeStatus(_context.ClusterName, _context.LocalShardName, existingMembership.Primary, NodeRole.None);

                    args.ChangeType = MembershipChangeArgs.MembershipChangeType.PrimaryDemoted;
                    args.ServerName = _context.LocalAddress;
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Info("electBasedMemSt.TriggerElectMech", "Primary node " + _context.LocalAddress.IpAddress.ToString() + " demoted in order to complete the take over election mechanism. ");
                    }

                    ((LocalShard)_shard).OnMembershipChanged(args);
                }
                break;
            }
        }
        /// <summary>
        /// Speculative Phase:
        /// 1. We search for the node with the latest op-log entry. This detail bears the highest value.
        /// 2. Next, if multiple nodes have the same op-log entry, we move onto the next step.
        /// 3. All those nodes which have the same op-log entry AND are connected to the CS are considered.
        /// 4. Highest priority from amongst these active nodes are taken into consideration.
        /// 5. If this node fulfills all of the above, it successfully passes the speculative phase.
        /// </summary>
        /// <param name="heartbeatReport"></param>
        /// <returns></returns>

        private bool ShouldIInitiateElection(LocalShardHeartbeatReporting heartbeatReport, Activity activity)
        {
            IList <Address> activeNodes = null;

            if (heartbeatReport != null)
            {
                activeNodes = heartbeatReport.GetReportTable.Keys.ToList();
                OperationId    maxOplog = null;
                IList <string> matchingOplogServerIPs = new List <string>();

                HeartbeatInfo localHeartbeat = heartbeatReport.GetHeartbeatInfo(_context.LocalAddress);
                if (localHeartbeat == null)
                {
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsWarnEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Warn(
                            "ElectionBasedMembershipStrategy.ShouldIInititateElections()",
                            "local node heartbeat is null");
                    }
                    return(false);
                }

                OperationId lastRepId = null;


                if (activity.Equals(Activity.TakeoverElectionsTriggered))
                {
                    HeartbeatInfo info = null;
                    if (_shard != null && _shard.Primary != null)
                    {
                        info = heartbeatReport.GetHeartbeatInfo(_shard.Primary.Address);
                    }
                }

                else
                {
                    ShardConfiguration sConfig       = _clusterConfigMgr.GetShardConfiguration(_context.LocalShardName);
                    int         configuredNodesCount = 0;
                    OperationId OpIdAtCS             = null;
                    if (sConfig != null && sConfig.Servers != null && sConfig.Servers.Nodes != null)
                    {
                        configuredNodesCount = sConfig.Servers.Nodes.Count;
                    }
                    if (configuredNodesCount > 0 && activeNodes != null && activeNodes.Count < configuredNodesCount)
                    {
                        ShardInfo   sInfo = null;
                        ClusterInfo cInfo = _context.ConfigurationSession.GetDatabaseClusterInfo(_context.ClusterName);
                        if (cInfo != null)
                        {
                            sInfo = cInfo.GetShardInfo(_context.LocalShardName);
                        }
                        if (sInfo != null)
                        {
                            OpIdAtCS = sInfo.LastOperationId;
                        }
                        if (OpIdAtCS > lastRepId)
                        {
                            if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                            {
                                LoggerManager.Instance.ShardLogger.Info(
                                    "electBasedMemSt.ShouldIInitElections()",
                                    "CS has an operation newer than my operation. Hence, waiting.");
                            }

                            if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsInfoEnabled)
                            {
                                if (OpIdAtCS != null)
                                {
                                    LoggerManager.Instance.ShardLogger.Info(
                                        "electBasedMemSt.ShouldIInitElections()",
                                        "Operation ID on the CS:- " + OpIdAtCS.ElectionId + ":"
                                        + OpIdAtCS.ElectionBasedSequenceId);
                                }
                                else
                                {
                                    LoggerManager.Instance.ShardLogger.Info(
                                        "electBasedMemSt.ShouldIInitElections()", "The operation ID at the CS is set to null.");
                                }
                                if (lastRepId != null)
                                {
                                    LoggerManager.Instance.ShardLogger.Info(
                                        "electBasedMemSt.ShouldIInitElections()", "Local node Operation ID:- " + lastRepId.ElectionId +
                                        ":" + lastRepId.ElectionBasedSequenceId);
                                }
                                else
                                {
                                    LoggerManager.Instance.ShardLogger.Info(
                                        "electBasedMemSt.ShouldIInitElections()", "The local node operation ID is set to null.");
                                }
                            }
                            //We maintain the last replicated operation log entry with the CS.
                            //If a node in a shard with older data (usually the previous secondary) is up before the node with the
                            //latest data(usually the previous primary), it waits for a configurable amount of time (2 minutes for
                            //now) before proceeding with the election procedure if it is still unable to detect a primary node.
                            //This way we give the node with the latest data a chance to become primary and therefore avoid data loss.
                            lock (_mutexOnWait)
                            {
                                Monitor.Wait(_mutexOnWait, _waitTimeout);
                            }
                        }
                    }

                    for (int i = 0; i < activeNodes.Count; i++)
                    {
                        HeartbeatInfo info           = heartbeatReport.GetHeartbeatInfo(activeNodes[i]);
                        OperationId   currIndexOplog = info.LastOplogOperationId;
                        if (currIndexOplog > maxOplog)
                        {
                            maxOplog = currIndexOplog;
                        }

                        if (((localHeartbeat.LastOplogOperationId == null && info.LastOplogOperationId == null) || localHeartbeat.LastOplogOperationId != null && localHeartbeat.LastOplogOperationId.Equals(info.LastOplogOperationId)) &&
                            info.CSStatus == ConnectivityStatus.Connected)
                        {
                            matchingOplogServerIPs.Add(activeNodes[i].IpAddress.ToString());
                        }
                    }
                }

                if (localHeartbeat.LastOplogOperationId != null && maxOplog != null && maxOplog > localHeartbeat.LastOplogOperationId && (lastRepId == null || maxOplog > lastRepId))
                {
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "Local operation log is behind max op log wrt " + _context.LocalShardName + " shard.");
                    }
                    if (maxOplog != null && LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "maxOplog: " + maxOplog.ElectionId + ":" + maxOplog.ElectionBasedSequenceId);
                    }
                    if (localHeartbeat.LastOplogOperationId != null && LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "local opLog (from the heartbeat): " + localHeartbeat.LastOplogOperationId.ElectionId + ":" + localHeartbeat.LastOplogOperationId.ElectionBasedSequenceId);
                    }
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled && lastRepId != null)
                    {
                        LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "LastOpFromOpLog (from the replication module): " + lastRepId.ElectionId + ":" + lastRepId.ElectionBasedSequenceId);
                    }

                    return(false);
                }
                else if (maxOplog == localHeartbeat.LastOplogOperationId || (lastRepId != null && lastRepId.Equals(maxOplog)))
                {
                    //if: there are multiple nodes that have the same oplog entry,
                    //decision will be made on the basis of the priorities.
                    //else: the node with the highest oplog entry will be considered eligible.
                    if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                    {
                        LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "Local operation log is equal to the max op log wrt " + _context.LocalShardName + " shard.");
                    }
                    if (maxOplog != null && (localHeartbeat != null && localHeartbeat.LastOplogOperationId != null))
                    {
                        if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                        {
                            LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "maxOplog: " + maxOplog.ElectionId + ":" + maxOplog.ElectionBasedSequenceId);
                        }
                        if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                        {
                            LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "local opLog (from the heartbeat): " + localHeartbeat.LastOplogOperationId.ElectionId + ":" + localHeartbeat.LastOplogOperationId.ElectionBasedSequenceId);
                        }
                        if (LoggerManager.Instance.ShardLogger != null && LoggerManager.Instance.ShardLogger.IsDebugEnabled && lastRepId != null)
                        {
                            LoggerManager.Instance.ShardLogger.Debug("electBasedMemSt.ShouldIInitiateElect()", "LastOpFromOpLog (from the replication module): " + lastRepId.ElectionId + ":" + lastRepId.ElectionBasedSequenceId);
                        }
                    }
                    if (matchingOplogServerIPs.Count > 0)
                    {
                        int highestRunningNodePriority = _manager.GetHighestNodePriority(activeNodes,
                                                                                         matchingOplogServerIPs);

                        if (highestRunningNodePriority.Equals(_manager.LocalServerPriority))
                        {
                            if (LoggerManager.Instance.ShardLogger != null &&
                                LoggerManager.Instance.ShardLogger.IsDebugEnabled)
                            {
                                LoggerManager.Instance.ShardLogger.Debug(
                                    "ElectionBasedMembershipStrategy.ShouldIInitiateElection()",
                                    "Node : " + _context.LocalAddress.IpAddress.ToString() + " in shard: " +
                                    _context.LocalShardName + " is eligible having priority: " +
                                    highestRunningNodePriority + " .");
                            }
                            return(true);
                        }
                        else
                        {
                            return(false);
                        }
                    }
                }
            }
            return(false);
        }