public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, ""); result.Results.Add("Stats", stats); return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } StringBuilder description = new StringBuilder(); // Traverse through each pattern set found in rule parameter foreach (var pattern in m_MatchPatterns) { Dictionary <ServiceCallItem, int> matchesFoundDict = new Dictionary <ServiceCallItem, int>(); int patternInstancesFound = 0; int lowXUIDInstancesFound = 0; // This first section reports on violations which are from batch calls made with not enough XUIDs in the request body foreach (ServiceCallItem thisItem in items) { Match match = Regex.Match(thisItem.m_uri, pattern.Key.ToString()); if (match.Success) { patternInstancesFound++; JObject requestBodyJSON = JObject.Parse(thisItem.m_reqBody); var values = requestBodyJSON[pattern.Value.ToString()] as JArray; if (values != null) { matchesFoundDict.Add(thisItem, values.Count); if (values.Count <= m_minBatchXUIDsPerBatchCall) { lowXUIDInstancesFound++; description.Clear(); description.AppendFormat("Batch call detected for endpoint for a small sized set of {0} XUIDs.", values.Count); result.AddViolation(ViolationLevel.Warning, description.ToString(), thisItem); } } } } // finished traversing calls made to endpoint m_patternInstancesFound.Add(new Tuple <int, int>(patternInstancesFound, lowXUIDInstancesFound)); } // end of foreach pattern in patterns var finalStats = PatternsFoundSumAsTuple(); result.Results.Add("Total Batch Calls", finalStats.Item1); result.Results.Add("Min. Users Allowed", m_minBatchXUIDsPerBatchCall); result.Results.Add("Calls Below Count", finalStats.Item2); result.Results.Add("% Below Count", (double)(finalStats.Item2) / finalStats.Item1); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); StringBuilder description = new StringBuilder(); m_avgCallsPerSecond = 1000.0 * 1.0 / stats.m_avgTimeBetweenReqsMs; m_callStdDeviationPerSecond = 1000.0 * 1.0 / Math.Sqrt(stats.m_varTimeBetweenReqsMs); const float factor = 2.0f; UInt32 burstSize = (UInt32)Math.Ceiling(m_avgCallsPerSecond) + (UInt32)Math.Ceiling(factor * m_callStdDeviationPerSecond); var allBurstsDetected = Utils.GetExcessCallsForTimeWindow(items, m_burstDetectionWindowMs, burstSize); // burst - is a list of calls (or just one call) that has exceeded the average requests per second rate foreach (var burst in allBurstsDetected) { if (burst.Count >= m_burstSizeToDetect) { description.Clear(); description.AppendFormat("Burst increase of {0} calls to endpoint found.", burst.Count); result.AddViolation(ViolationLevel.Warning, description.ToString(), burst); } } if (double.IsInfinity(m_avgCallsPerSecond)) { result.Results.Add("Avg. Calls Per Sec.", "N/A"); result.Results.Add("Std. Deviation", "N/A"); result.Results.Add("Burst Size", m_burstSizeToDetect); result.Results.Add("Burst Window", m_burstDetectionWindowMs); result.Results.Add("Total Bursts", result.ViolationCount); } else { result.Results.Add("Avg. Calls Per Sec.", m_avgCallsPerSecond); result.Results.Add("Std. Deviation", m_callStdDeviationPerSecond); result.Results.Add("Burst Size", m_burstSizeToDetect); result.Results.Add("Burst Window", m_burstDetectionWindowMs); result.Results.Add("Total Bursts", result.ViolationCount); } return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { bool richpresenceFound = false; var richPresenceJsonObj = new { activity = new Object() }; foreach (var call in items) { var richPresenceResult = JsonConvert.DeserializeAnonymousType(call.m_reqBody, richPresenceJsonObj); if (richPresenceResult != null && richPresenceResult.activity != null) { richpresenceFound = true; break; } } RuleResult result = InitializeResult(DisplayName, ""); if (!richpresenceFound) { result.Violations.Add(new Violation()); } return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); m_stats = stats; m_endpointSustainedViolations = 0; m_endpointBurstViolations = 0; // For set of limits, look through items to determine where excess calls occurred foreach (var limits in m_rateLimits) { // Filter the full list of service calls to those which apply to this set of limits List <ServiceCallItem> applicableCalls = items.Where(serviceCall => { foreach (var subpath in limits.m_applicableSubpaths) { var subpathRegex = new Regex("^" + Regex.Escape(subpath).Replace("\\?", ".").Replace("\\*", ".*") + "$"); if (subpathRegex.IsMatch(new Uri(serviceCall.m_uri).AbsolutePath)) { return(true); } } return(false); }).ToList(); var sustainedExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(applicableCalls, limits.m_sustainedTimePeriodSeconds * 1000, limits.m_sustainedCallLimit); var burstExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(applicableCalls, limits.m_burstTimePeriodSeconds * 1000, limits.m_burstCallLimit); foreach (var excessCalls in sustainedExcessCallsPerWindow) { if (excessCalls.Count >= limits.m_sustainedCallLimit * 10) { var desc = $"Exceeding rate limits for '{limits.m_description}' required for title certification( limit of {limits.m_sustainedCallLimit * 10} calls with {excessCalls.Count} calls in {limits.m_sustainedTimePeriodSeconds} seconds). Failure to adhere to the specified limits may block a title from release, and in-production issues with released titles may result in service suspension up to and including title removal."; result.AddViolation(ViolationLevel.Error, desc, excessCalls); } else { var desc = $"Call frequency above the sustained call limit for '{limits.m_description}' (limit of {limits.m_sustainedCallLimit} exceeded with {excessCalls.Count} calls in {limits.m_sustainedTimePeriodSeconds} seconds)."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); } m_endpointSustainedViolations++; } foreach (var excessCalls in burstExcessCallsPerWindow) { var desc = $"Call frequency above the burst call limit for '{limits.m_description}' (limit of {limits.m_burstCallLimit} exceeded with {excessCalls.Count} calls in {limits.m_burstTimePeriodSeconds} seconds)."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); m_endpointBurstViolations++; } // The following is information that would only be useful for internal purposes. if (RulesEngine.m_isInternal) { UInt64 avgTimeBetweenReqsMs = stats.m_avgTimeBetweenReqsMs; UInt64 avgElapsedCallTimeMs = stats.m_avgElapsedCallTimeMs; UInt64 maxElapsedCallTimeMs = stats.m_maxElapsedCallTimeMs; if (avgTimeBetweenReqsMs > 0 && avgTimeBetweenReqsMs < limits.m_avgTimeBetweenReqsMs) { result.AddViolation(ViolationLevel.Warning, "Average time of " + avgTimeBetweenReqsMs + "ms between calls is too short"); } if (avgElapsedCallTimeMs > 0 && avgElapsedCallTimeMs > limits.m_avgElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "Calls are taking longer than expected to return " + avgElapsedCallTimeMs + "ms"); } if (maxElapsedCallTimeMs > 0 && maxElapsedCallTimeMs > limits.m_maxElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "The maximum call time for calls is greater than allowed " + maxElapsedCallTimeMs + "ms"); } } } result.Results.Add("Total Calls", m_stats == null ? 0 : m_stats.m_numCalls); result.Results.Add("Times Sustained Exceeded", m_endpointSustainedViolations); result.Results.Add("Times Burst Exceeded", m_endpointBurstViolations); return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); if (items.Count() == 0) { return(result); } //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } List <ServiceCallItem> allRepeats = new List <ServiceCallItem>(); var nonShoulderTapItems = items.Where(item => item.m_isShoulderTap == false); List <Sequence> sequences = new List <Sequence>(); foreach (ServiceCallItem thisItem in nonShoulderTapItems) { if (thisItem.m_reqHeader.Contains("SocialManager")) { continue; } // Used to skip over already analyzed repeated calls. if (allRepeats.Contains(thisItem)) { continue; } // Discover all repeats to thisItem var myRepeats = from item in nonShoulderTapItems where ((item != thisItem) && (item.m_reqBodyHash == thisItem.m_reqBodyHash) && (item.m_uri == thisItem.m_uri)) select item; if (myRepeats.Count() > 1) { var deltas = BuildDeltas(myRepeats.ToList()); sequences.AddRange(GenerateSequences(deltas, m_sameDeltaThresholdPercentage)); allRepeats.AddRange(myRepeats); } } if (sequences == null) { return(result); } //Process Violations StringBuilder description = new StringBuilder(); foreach (var sequence in sequences.OrderByDescending(s => s.Length)) { description.Clear(); description.AppendFormat("Polling detected in a sequence of {0} calls at a rate of approximately {1} min:sec", sequence.Length, TimeSpan.FromTicks((long)sequence.m_averageDelta).ToString(@"mm\:ss\.fff")); result.AddViolation(ViolationLevel.Warning, description.ToString(), sequence.m_seqCalls); } result.Results.Add("Polling Sequences Found", result.ViolationCount); return(result); }
// Parameters: // - items: List of the ServiceCallItems that describe calls to this rule's endpoint // - stats: Simple statistics that were computed while this set of calls was being processed. public abstract RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats);
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); m_totalCalls = items.Count(); m_throttledCallsCount = items.Where(call => call.m_httpStatusCode == 429).Count(); // We need to search over all of the calls to the endpoint for (int i = 0; i < m_totalCalls;) { var call = items.ElementAt(i); // If its not a throttled call, move to the next call if (call.m_httpStatusCode != 429) { ++i; continue; } // If it is throttled, start a list List <ServiceCallItem> throttledCallSet = new List <ServiceCallItem>(); throttledCallSet.Add(items.ElementAt(i)); var throttledCall = throttledCallSet.First(); string throttleGuidline = string.Empty; try { if (!string.IsNullOrEmpty(throttledCall.m_rspBody)) { JObject response = JObject.Parse(throttledCall.m_rspBody); if (response["maxRequests"] != null && response["periodInSeconds"] != null) { throttleGuidline = $"Allowed: {response["maxRequests"]} over {response["periodInSeconds"]} seconds."; } } } catch (Exception) { } // If there are 2 or more throttled calls in a row the title is not properly handling the response while (++i < m_throttledCallsCount) { var nextCall = items.ElementAt(i); if (call.m_httpStatusCode != 429) { ++i; break; } else { throttledCallSet.Add(call); } } // One call is a warning as we expect that they back off after getting the 429 response if (throttledCallSet.Count == 1) { result.AddViolation(ViolationLevel.Warning, "Throttled call detected on endpoint. " + throttleGuidline, throttledCall); } // More that one in a row means that the title didn't handle the 429 and we want them to fix that. else { result.AddViolation(ViolationLevel.Error, "Sequence of throttled calls detected on endpoint. " + throttleGuidline, throttledCallSet); } throttledCallSet.Clear(); } result.Results.Add("Total Calls", TotalCalls); result.Results.Add("Throttled Calls", ThrottledCallCount); result.Results.Add("Percentage", ((double)ThrottledCallCount) / TotalCalls); return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { var result = InitializeResult(""); // Check events type if (items.Count() == 0) { return(result); } var mpEvents = items.Where(e => e.m_eventName.Contains("MultiplayerRound")); var mpStartEvents = mpEvents.Where(e => e.m_eventName.Contains("Start")); var mpEndEvents = mpEvents.Where(e => e.m_eventName.Contains("End")); var debugData = mpEvents.Select(e => new { Name = e.m_eventName, ID = e.m_multiplayerCorrelationId }); var matchEvents = mpEvents.GroupBy(e => e.m_multiplayerCorrelationId); // Edge case if a correlation id was reused var duplicates = matchEvents.Where(g => g.Count() > 2); var matchedTotalCount = matchEvents.Count(g => g.Count() == 2); var mismatchedEvents = matchEvents.Where(g => g.Count() == 1); var mismatchedStartEventCount = mismatchedEvents.Count(g => g.Any(c => c.m_eventName.Contains("Start"))); var mismatchedEndEventCount = mismatchedEvents.Count(g => g.Any(c => c.m_eventName.Contains("End"))); foreach (var set in duplicates) { var starts = set.Count(e => e.m_eventName.Contains("Start")); var ends = set.Count(e => e.m_eventName.Contains("End")); if (starts > ends) { mismatchedStartEventCount += (starts - ends); } else if (starts < ends) { mismatchedEndEventCount += (ends - starts); } } var mismatchedTotalCount = mismatchedEvents.Count(); double startToEndRatio = (double)mpStartEvents.Count() / (double)mpEndEvents.Count(); double unmatchedStartRatio = (double)mismatchedStartEventCount / (double)mpStartEvents.Count(); double unmatchedEndRatio = (double)mismatchedEndEventCount / (double)mpEndEvents.Count(); if (startToEndRatio < .9 || startToEndRatio > 1.1) { result.AddViolation(ViolationLevel.Error, $"Ratio of Start and End events is {startToEndRatio:0.0%}. Allowed range is 90-110%."); } if (unmatchedStartRatio > .1) { result.AddViolation(ViolationLevel.Error, $"{unmatchedStartRatio:0.0%} of MultiplayerRoundStart events were unmatched. Allowed amount is less than 10%."); } if (unmatchedEndRatio > .1) { result.AddViolation(ViolationLevel.Error, $"{unmatchedEndRatio:0.0%} of MultiplayerRoundEnd events were unmatched. Allowed amount is less than 10%."); } result.Results.Add("TotalStartEvents", mpStartEvents.Count()); result.Results.Add("TotalEndEvents", mpEndEvents.Count()); result.Results.Add("StartToEndRatio", startToEndRatio); result.Results.Add("UnmatchedStartEventCount", mismatchedStartEventCount); result.Results.Add("UnmatchedStartPercentage", unmatchedStartRatio); result.Results.Add("UnmatchedEndEventCount", mismatchedEndEventCount); result.Results.Add("UnmatchedEndPercentage", unmatchedEndRatio); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); if (items.Count() == 0) { return(result); } //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } StringBuilder description = new StringBuilder(); List <ServiceCallItem> repeats = new List <ServiceCallItem>(); m_totalCallsChecked = items.Count(); foreach (ServiceCallItem thisItem in items.Where(item => item.m_isShoulderTap == false)) { if (thisItem.m_reqHeader.Contains("SocialManager")) { continue; } if (repeats.Contains(thisItem)) { continue; } var timeWindow = from item in items.Where(item => item.m_isShoulderTap == false) where (item.m_reqTimeUTC > thisItem.m_reqTimeUTC && ((item.m_reqTimeUTC - thisItem.m_reqTimeUTC) / TimeSpan.TicksPerMillisecond) < m_minAllowedRepeatIntervalMs) select item; List <ServiceCallItem> repeatedCalls = new List <ServiceCallItem>(); repeatedCalls.Add(thisItem); foreach (var call in timeWindow) { if (thisItem.m_reqBodyHash == call.m_reqBodyHash && thisItem.m_uri == call.m_uri) { repeatedCalls.Add(call); } } if (repeatedCalls.Count > 1) { description.Clear(); description.AppendFormat("Repeated call found {0} other times in calls to endpoint.", repeatedCalls.Count, thisItem.m_id); result.AddViolation(ViolationLevel.Warning, description.ToString(), repeatedCalls); repeats.AddRange(repeatedCalls); } } m_numberOfRepeats = repeats.Count; result.Results.Add("Total Calls", m_totalCallsChecked); result.Results.Add("Duplicates", m_numberOfRepeats); result.Results.Add("Percentage", ((double)m_numberOfRepeats) / m_totalCallsChecked); return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } StringBuilder description = new StringBuilder(); // Traverse through each pattern set found in rule parameter foreach (var pattern in m_MatchPatterns) { Dictionary <ServiceCallItem, int> matchesFoundDict = new Dictionary <ServiceCallItem, int>(); foreach (ServiceCallItem thisItem in items) { Match match = Regex.Match(thisItem.m_uri, pattern.Key); if (match.Success) { JObject requestBodyJSON = JObject.Parse(thisItem.m_reqBody); var values = requestBodyJSON.SelectToken(pattern.Value) as JArray; if (values != null) { matchesFoundDict.Add(thisItem, values.Count); } } } // finished traversing calls made to endpoint m_totalBatchCallCount = (UInt32)matchesFoundDict.Count; // For all the matches found, report on batch sets which happened within a specific time window int numDictItems = matchesFoundDict.Count; if (numDictItems >= 2) { int startWindowIdx = 0; List <ServiceCallItem> callsWithinWindow = new List <ServiceCallItem>(); int totalXUIDsForWindow = 0; totalXUIDsForWindow += matchesFoundDict.Values.ElementAt(startWindowIdx); callsWithinWindow.Add(matchesFoundDict.Keys.ElementAt(startWindowIdx)); for (int endWindowIdx = 1; endWindowIdx < matchesFoundDict.Count(); ++endWindowIdx) { UInt64 timeElapsed = (UInt64)Math.Abs((float) (matchesFoundDict.Keys.ElementAt(endWindowIdx).m_reqTimeUTC - matchesFoundDict.Keys.ElementAt(startWindowIdx).m_reqTimeUTC) / TimeSpan.TicksPerMillisecond); if (timeElapsed <= m_BatchSetDetectionWindowsMs) { callsWithinWindow.Add(matchesFoundDict.Keys.ElementAt(endWindowIdx)); totalXUIDsForWindow += matchesFoundDict.Values.ElementAt(endWindowIdx); } else //exceeded window { if (callsWithinWindow.Count >= 2) { result.AddViolation(ViolationLevel.Warning, $"A set of {callsWithinWindow.Count} Batch Calls was found within a time window of ({m_BatchSetDetectionWindowsMs}ms) to endpoint. Total XUID count ({totalXUIDsForWindow}). Consider combining into one call.", callsWithinWindow); } startWindowIdx = endWindowIdx; // shift window //reset figures totalXUIDsForWindow = 0; callsWithinWindow.Clear(); } } // in case we exited the last for early because we never exceeded the time window, then call // the following function once more to handle any dangling reports. if (callsWithinWindow.Count >= 2) { result.AddViolation(ViolationLevel.Warning, $"A set of {callsWithinWindow.Count} Batch Calls was found within a time window of ({m_BatchSetDetectionWindowsMs}ms) to endpoint. Total XUID count ({totalXUIDsForWindow}). Consider combining into one call.", callsWithinWindow); } } } // end of foreach pattern in patterns result.Results.Add("Total Batch Calls", m_totalBatchCallCount); result.Results.Add("Allowed Time Between Calls in ms", m_BatchSetDetectionWindowsMs); result.Results.Add("Times Exceeded", result.ViolationCount); result.Results.Add("Potential Reduced Call Count", m_totalBatchCallCount - result.ViolationCount); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); m_stats = stats; m_endpointSustainedViolations = 0; m_endpointBurstViolations = 0; // Look through items to determine where excess calls occurred var sustainedExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_sustainedTimePeriodSeconds * 1000, m_sustainedCallLimit); var burstExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_burstTimePeriodSeconds * 1000, m_burstCallLimit); foreach (var excessCalls in sustainedExcessCallsPerWindow) { if (excessCalls.Count >= m_sustainedCallLimit * 10) { var desc = $"Exceeding service rate limits required for title certification( limit of {m_sustainedCallLimit * 10} calls with {excessCalls.Count} calls in {m_sustainedTimePeriodSeconds} seconds). Failure to adhere to the specified limits may block a title from release, and in-production issues with released titles may result in service suspension up to and including title removal."; result.AddViolation(ViolationLevel.Error, desc, excessCalls); } else { var desc = $"Call frequency above the sustained call limit of {m_sustainedCallLimit} with {excessCalls.Count} calls in {m_sustainedTimePeriodSeconds} seconds to endpoint."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); } m_endpointSustainedViolations++; } foreach (var excessCalls in burstExcessCallsPerWindow) { var desc = $"Call frequency above the burst call limit of {m_burstCallLimit} with {excessCalls.Count} calls {m_burstTimePeriodSeconds} seconds to endpoint."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); m_endpointBurstViolations++; } // The following is information that would only be useful for internal purposes. if (RulesEngine.m_isInternal) { UInt64 avgTimeBetweenReqsMs = stats.m_avgTimeBetweenReqsMs; UInt64 avgElapsedCallTimeMs = stats.m_avgElapsedCallTimeMs; UInt64 maxElapsedCallTimeMs = stats.m_maxElapsedCallTimeMs; if (avgTimeBetweenReqsMs > 0 && avgTimeBetweenReqsMs < m_avgTimeBetweenReqsMs) { result.AddViolation(ViolationLevel.Warning, "Average time of " + avgTimeBetweenReqsMs + "ms between calls is too short"); } if (avgElapsedCallTimeMs > 0 && avgElapsedCallTimeMs > m_avgElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "Calls are taking longer than expected to return " + avgElapsedCallTimeMs + "ms"); } if (maxElapsedCallTimeMs > 0 && maxElapsedCallTimeMs > m_maxElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "The maximum call time for calls is greater than allowed " + maxElapsedCallTimeMs + "ms"); } } result.Results.Add("Total Calls", m_stats == null ? 0 : m_stats.m_numCalls); result.Results.Add("Sustained Call Period", m_sustainedTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Sustained Call Limit", m_sustainedCallLimit); result.Results.Add("Times Sustained Exceeded", m_endpointSustainedViolations); result.Results.Add("Burst Call Period", m_burstTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Burst Call Limit", m_burstCallLimit); result.Results.Add("Times Burst Exceeded", m_endpointBurstViolations); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); StringBuilder description = new StringBuilder(); m_stats = stats; m_endpointSustainedViolations = 0; m_endpointBurstViolations = 0; // Look through items to determine where excess calls occurred var sustainedExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_sustainedTimePeriodSeconds * 1000, (UInt32)(m_sustainedCallLimit * .9)); var burstExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_burstTimePeriodSeconds * 1000, (UInt32)(m_burstCallLimit * .9)); foreach (var excessCalls in sustainedExcessCallsPerWindow) { description.Clear(); if (excessCalls.Count < m_sustainedCallLimit) { description.AppendFormat("Call frequency nearing the sustained call limit of {0} with {1} calls to endpoint.", m_sustainedCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Warning, description.ToString(), excessCalls); } else { m_endpointSustainedViolations++; description.AppendFormat("Call frequency above the sustained call limit of {0} with {1} calls to endpoint.", m_sustainedCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Error, description.ToString(), excessCalls); } } foreach (var excessCalls in burstExcessCallsPerWindow) { description.Clear(); if (excessCalls.Count < m_burstCallLimit) { description.AppendFormat("Call frequency nearing the burst call limit of {0} with {1} calls to endpoint.", m_burstCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Warning, description.ToString(), excessCalls); } else { m_endpointBurstViolations++; description.AppendFormat("Call frequency above the burst call limit of {0} with {1} calls to endpoint.", m_burstCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Error, description.ToString(), excessCalls); } } // The following is information that would only be useful for internal purposes. if (RulesEngine.m_isInternal) { UInt64 avgTimeBetweenReqsMs = stats.m_avgTimeBetweenReqsMs; UInt64 avgElapsedCallTimeMs = stats.m_avgElapsedCallTimeMs; UInt64 maxElapsedCallTimeMs = stats.m_maxElapsedCallTimeMs; if (avgTimeBetweenReqsMs > 0 && avgTimeBetweenReqsMs < m_avgTimeBetweenReqsMs) { result.AddViolation(ViolationLevel.Warning, "Average time of " + avgTimeBetweenReqsMs + "ms between calls is too short"); } if (avgElapsedCallTimeMs > 0 && avgElapsedCallTimeMs > m_avgElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "Calls are taking longer than expected to return " + avgElapsedCallTimeMs + "ms"); } if (maxElapsedCallTimeMs > 0 && maxElapsedCallTimeMs > m_maxElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "The maximum call time for calls is greater than allowed " + maxElapsedCallTimeMs + "ms"); } } result.Results.Add("Total Calls", m_stats == null ? 0 : m_stats.m_numCalls); result.Results.Add("Sustained Call Period", m_sustainedTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Sustained Call Limit", m_sustainedCallLimit); result.Results.Add("Times Sustained Exceeded", m_endpointSustainedViolations); result.Results.Add("Burst Call Period", m_burstTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Burst Call Limit", m_burstCallLimit); result.Results.Add("Times Burst Exceeded", m_endpointBurstViolations); return(result); }