/// <summary> /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes. /// </summary> /// <param name="context">The modified document context.</param> /// <param name="previous">The original document model.</param> /// <returns>A list of messages from the comparison.</returns> public override IEnumerable <ComparisonMessage> Compare(ComparisonContext context, SwaggerBase previous) { var priorOperation = previous as Operation; var currentRoot = context.CurrentRoot as ServiceDefinition; var previousRoot = context.PreviousRoot as ServiceDefinition; if (priorOperation == null) { throw new ArgumentException("previous"); } base.Compare(context, previous); if (!OperationId.Equals(priorOperation.OperationId)) { context.LogBreakingChange(ComparisonMessages.ModifiedOperationId, priorOperation.OperationId, OperationId); } CheckParameters(context, priorOperation); if (Responses != null && priorOperation.Responses != null) { foreach (var response in Responses) { var oldResponse = priorOperation.FindResponse(response.Key, priorOperation.Responses); context.PushProperty(response.Key); if (oldResponse == null) { context.LogBreakingChange(ComparisonMessages.AddingResponseCode, response.Key); } else { response.Value.Compare(context, oldResponse); } context.Pop(); } foreach (var response in priorOperation.Responses) { var newResponse = FindResponse(response.Key, Responses); if (newResponse == null) { context.PushProperty(response.Key); context.LogBreakingChange(ComparisonMessages.RemovedResponseCode, response.Key); context.Pop(); } } } return(context.Messages); }
public override bool Equals(Base other) { if (other == null) { return(false); } if (other is ModuleOperation ModuleOperation) { return(OperationId.Equals(ModuleOperation.OperationId) && ModuleId.Equals(ModuleOperation.ModuleId)); } return(false); }
/// <summary> /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes. /// </summary> /// <param name="context">The modified document context.</param> /// <param name="previous">The original document model.</param> /// <returns>A list of messages from the comparison.</returns> public override IEnumerable <ComparisonMessage> Compare( ComparisonContext <ServiceDefinition> context, Operation previous ) { var priorOperation = previous; var currentRoot = context.CurrentRoot; var previousRoot = context.PreviousRoot; if (priorOperation == null) { throw new ArgumentException("previous"); } base.Compare(context, previous); if (!OperationId.Equals(priorOperation.OperationId)) { context.LogBreakingChange(ComparisonMessages.ModifiedOperationId, priorOperation.OperationId, OperationId); } Extensions.TryGetValue("x-ms-long-running-operation", out var currentLongrunningOperationValue); priorOperation.Extensions.TryGetValue("x-ms-long-running-operation", out var priorLongrunningOperationValue); currentLongrunningOperationValue = currentLongrunningOperationValue == null ? false : currentLongrunningOperationValue; priorLongrunningOperationValue = priorLongrunningOperationValue == null ? false : priorLongrunningOperationValue; if (!currentLongrunningOperationValue.Equals(priorLongrunningOperationValue)) { context.LogBreakingChange(ComparisonMessages.XmsLongRunningOperationChanged); } CheckParameters(context, priorOperation); if (Responses != null && priorOperation.Responses != null) { context.PushProperty("responses"); foreach (var response in Responses) { var oldResponse = priorOperation.FindResponse(response.Key, priorOperation.Responses); context.PushProperty(response.Key); if (oldResponse == null) { context.LogBreakingChange(ComparisonMessages.AddingResponseCode, response.Key); } else { response.Value.Compare(context, oldResponse); } context.Pop(); } foreach (var response in priorOperation.Responses) { var newResponse = this.FindResponse(response.Key, this.Responses); if (newResponse == null) { context.PushProperty(response.Key); context.LogBreakingChange(ComparisonMessages.RemovedResponseCode, response.Key); context.Pop(); } } context.Pop(); } return(context.Messages); }
/// <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); }