/// <summary>
        /// Run a distributed (tiered) query based on the parameters in the original request. Split the sources into N blocks (N is determined
        /// by the MaxFanout setting). Client will select a (random) leader for each block and send the request downstream. Client will merge all responses
        /// and in the case of failure will include diagnostics for each server in the chunk
        /// </summary>
        /// <param name="counterName">Name of the counter to query.</param>
        /// <param name="request">Base request to be distributed into blocks.</param>
        /// <param name="queryParameters">Query parameters for the counter.</param>
        /// <param name="dimensionSet">DimensionSet used to quickly merge per-sample query dimensions.</param>
        /// <returns>Aggregated response optionally returning per-source diagnostics. </returns>
        public async Task <CounterQueryResponse> CounterQuery(string counterName, TieredRequest request,
                                                              IDictionary <string, string> queryParameters,
                                                              DimensionSet dimensionSet)
        {
            if (string.IsNullOrEmpty(counterName))
            {
                throw new ArgumentException("No counter name specified", "counterName");
            }

            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if (dimensionSet == null)
            {
                throw new ArgumentNullException("dimensionSet");
            }

            var counterAggregator = new CounterAggregator(dimensionSet);

            // if the client requested a percentile and there are multiple sources we let the aggregator apply that filtering after data collection
            if (HasSources(request))
            {
                queryParameters = counterAggregator.ApplyPercentileCalculationAggregation(queryParameters);
            }

            var command = Protocol.BuildCounterRequestCommand(RestCommands.CounterQueryCommand, counterName, queryParameters);

            return(await this.Execute(request, command, counterAggregator.AddMachineResponse,
                                      () => counterAggregator.GetResponse(ShouldMergeTimeBuckets(queryParameters))));
        }
        /// <summary>
        /// Constructor that validates the query request and initializes internal state and fixes/cleans up the individual queries
        /// </summary>
        public BatchResponseAggregator(BatchQueryRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if (request.Queries == null || request.Queries.Count == 0)
            {
                throw new ArgumentException("No queries provided", "request");
            }

            foreach (var counterRequest in request.Queries)
            {
                if (string.IsNullOrEmpty(counterRequest.UserContext))
                {
                    counterRequest.UserContext = Guid.NewGuid().ToString("D");
                }
                else if (this.requestMapping.ContainsKey(counterRequest.UserContext))
                {
                    throw new ArgumentException("Duplicate user context in request");
                }

                var counterAggregator = new CounterAggregator(counterRequest.QueryParameters);
                var shouldMergeSamples = DistributedQueryClient.ShouldMergeTimeBuckets(counterRequest.QueryParameters);

                counterRequest.QueryParameters = counterAggregator.ApplyPercentileCalculationAggregation(counterRequest.QueryParameters);
                this.requestMapping.Add(counterRequest.UserContext, new Tuple<bool, CounterAggregator>(shouldMergeSamples, counterAggregator));
            }
        }
        /// <summary>
        /// Constructor that validates the query request and initializes internal state and fixes/cleans up the individual queries
        /// </summary>
        public BatchResponseAggregator(BatchQueryRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            if (request.Queries == null || request.Queries.Count == 0)
            {
                throw new ArgumentException("No queries provided", "request");
            }

            foreach (var counterRequest in request.Queries)
            {
                if (string.IsNullOrEmpty(counterRequest.UserContext))
                {
                    counterRequest.UserContext = Guid.NewGuid().ToString("D");
                }
                else if (this.requestMapping.ContainsKey(counterRequest.UserContext))
                {
                    throw new ArgumentException("Duplicate user context in request");
                }

                var counterAggregator  = new CounterAggregator(counterRequest.QueryParameters);
                var shouldMergeSamples = DistributedQueryClient.ShouldMergeTimeBuckets(counterRequest.QueryParameters);

                counterRequest.QueryParameters = counterAggregator.ApplyPercentileCalculationAggregation(counterRequest.QueryParameters);
                this.requestMapping.Add(counterRequest.UserContext, new Tuple <bool, CounterAggregator>(shouldMergeSamples, counterAggregator));
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Merge the local Query request and the distributed tiered response into a single response
        /// </summary>
        private static CounterQueryResponse MergeResponses(IDictionary<string, string> queryParameters,
                                                           CounterQueryResponse localResponse, bool mergeTimeBuckets,
                                                           CounterQueryResponse distributedResponse)
        {
            // no distributed response, just use the local one
            if (distributedResponse == null)
            {
                return localResponse;
            }

            var sampleMerger = new CounterAggregator(queryParameters ?? new Dictionary<string, string>(0));
            sampleMerger.AddMachineResponse(localResponse);
            sampleMerger.AddMachineResponse(distributedResponse);

            // response code from this server is:
            //  200: *Anyone* has data
            //  40X: *Everyone failed to get data, but everyone has the same response code
            //  409: Mixed error codes ("Conflict" used liberally)
            HttpStatusCode responseCode;
            if ((localResponse.Samples != null && localResponse.Samples.Count > 0) ||
                (distributedResponse.Samples != null && distributedResponse.Samples.Count > 0))
            {
                responseCode = HttpStatusCode.OK;
            }
            else
            {
                responseCode = distributedResponse.RequestDetails != null &&
                               distributedResponse.RequestDetails.Count > 0 &&
                               distributedResponse.RequestDetails.All(d => d.HttpResponseCode == (int)localResponse.HttpResponseCode)
                                   ? (HttpStatusCode)localResponse.HttpResponseCode
                                   : HttpStatusCode.Conflict;
            }

            var mergedResponse = sampleMerger.GetResponse(mergeTimeBuckets);
            mergedResponse.HttpResponseCode = (short)responseCode;
            return mergedResponse;
        }