private async Task <bool> QuerySources(IList <PersistedDataSource> sources)
        {
            Events.Write.BeginQuerySources(sources);

            var success         = false;
            var transferRequest =
                new TransferRequest
            {
                DataType  = PersistedDataProtocol.GetPersistedTypeCodeFromType(typeof(TInternal)),
                Timeout   = (this.Timeout.Seconds * 9) / 10,   // 90% of timeout goes to the child
                MaxFanout = this.MaxFanout,
                Sources   = new List <string>()
            };

            foreach (var source in sources)
            {
                source.Status = PersistedDataSourceStatus.Unknown;
                transferRequest.Sources.Add(source.Name);
            }

            var serverOffset   = this.randomNumberGenerator.Next(sources.Count);
            var selectedSource = sources[serverOffset];

            var request = new HttpRequestMessage(HttpMethod.Post, this.CreateRequestUri(selectedSource.Name));

            using (var ms = (this.memoryStreamManager.GetStream()))
                using (var writeStream = new WriterStream(ms, this.memoryStreamManager))
                {
                    var writer = writeStream.CreateCompactBinaryWriter();
                    writer.Write(transferRequest);
                    request.Content = new ByteArrayContent(ms.ToArray());
                }

            try
            {
                Events.Write.BeginSendSourceQuery(selectedSource, request.RequestUri);
                var response =
                    await this.httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

                Events.Write.EndSendSourceQuery(selectedSource, (int)response.StatusCode, response.ReasonPhrase);

                if (!response.IsSuccessStatusCode)
                {
                    switch (response.StatusCode)
                    {
                    case HttpStatusCode.NotFound:
                        foreach (var source in sources)
                        {
                            source.Status = PersistedDataSourceStatus.Unavailable;
                        }
                        return(true);

                    default:
                        return(false);
                    }
                }

                Events.Write.BeginReceiveSourceResponseBody(selectedSource);
                var length = response.Content.Headers.ContentLength ?? 0;
                using (var dataStream = this.memoryStreamManager.GetStream("PersistedDataAggregator/Http/Read",
                                                                           (int)length, true))
                    using (var responseStream = await response.Content.ReadAsStreamAsync())
                    {
                        responseStream.CopyTo(dataStream);
                        dataStream.Position = 0;
                        using (var dataReader =
                                   new PersistedDataReader(dataStream, this.memoryStreamManager, this.DimensionSet))
                        {
                            Events.Write.EndReceiveSourceResponseBody(selectedSource, (int)dataStream.Length);

                            success = true;
                            if (dataStream.Length == 0)
                            {
                                Events.Write.EmptyResponseReceivedFromSource(selectedSource);
                                // Over the line. Mark it zero. Or just available, but empty (which is okay!)
                                foreach (var source in sources)
                                {
                                    source.Status = PersistedDataSourceStatus.Available;
                                }
                            }
                            else
                            {
                                try
                                {
                                    this.UnpackResponse(dataReader);
                                }
                                catch (PersistedDataException ex)
                                {
                                    Events.Write.PersistedDataExceptionFromSource(selectedSource, ex);
                                    success = false;
                                }
                            }
                        }
                    }
            }
            catch (OperationCanceledException) { }
            catch (Exception ex)
            {
                if (ex is HttpRequestException || ex is IOException || ex is WebException || ex is ObjectDisposedException)
                {
                    Events.Write.HttpExceptionFromSource(selectedSource, ex);
                }
                else
                {
                    throw;
                }
            }

            Events.Write.EndQuerySources(success, sources);
            return(success);
        }
示例#2
0
        public override async Task<Response> ProcessRequest(Request request)
        {
            var counterName = request.Path;
            if (string.IsNullOrEmpty(counterName))
            {
                return request.CreateErrorResponse(HttpStatusCode.BadRequest, "Path or command is invalid.");
            }

            if (request.QueryParameters.Count != 2
                || !request.QueryParameters.ContainsKey(ReservedDimensions.StartTimeDimension)
                || !request.QueryParameters.ContainsKey(ReservedDimensions.EndTimeDimension))
            {
                return request.CreateErrorResponse(HttpStatusCode.BadRequest,
                                                   "Invalid query parameters. Must specify only start and end time.");
            }

            DateTime start, end;
            if (!DateTime.TryParse(request.QueryParameters[ReservedDimensions.StartTimeDimension], out start))
            {
                return request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid start time.");
            }
            if (!DateTime.TryParse(request.QueryParameters[ReservedDimensions.EndTimeDimension], out end))
            {
                return request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid end time.");
            }

            var message = new TransferRequest();
            if (request.HasInputBody)
            {
                message = await request.ReadInputBody<TransferRequest>();
            }

            var ms = request.GetStream();

            // This is a tiered aggregation request, handle it separately.
            if (message.Sources != null && message.Sources.Count > 1)
            {
                try
                {
                    using (var aggregator =
                        PersistedDataProtocol.CreateAggregatorForSampleType(message.DataType, counterName,
                                                                            message.Sources, start, end,
                                                                            dataManager.MemoryStreamManager))
                    {
                        if (message.Timeout > 0)
                        {
                            aggregator.Timeout = TimeSpan.FromSeconds(message.Timeout);
                        }
                        if (message.MaxFanout > 0)
                        {
                            aggregator.MaxFanout = (int)message.MaxFanout;
                        }

                        // This shouldn't happen, given that one of the servers we want to talk to is.. us.
                        if (!await aggregator.Run())
                        {
                            ms.Dispose();
                            return request.CreateErrorResponse(HttpStatusCode.InternalServerError,
                                                               "All child requests failed.");
                        }
                        if (!aggregator.WriteToStream(ms))
                        {
                            // TODO: If we have no results but none of our queries failed we can definitively 404,
                            //       for now lazy.
                            ms.Dispose();
                            return request.CreateErrorResponse(HttpStatusCode.InternalServerError,
                                                               "No cntent matched.");
                        }
                    }
                }
                catch (ArgumentException)
                {
                    ms.Dispose();
                    return request.CreateErrorResponse(HttpStatusCode.BadRequest, "Request parameters are invalid.");
                }
            }
            else
            {
                var counter = this.dataManager.GetCounter<Counter>(counterName);
                if (counter == null || !counter.SerializeData(start, end, ms))
                {
                    ms.Dispose();
                    return request.CreateErrorResponse(HttpStatusCode.NotFound, "The requested data is not available.");
                }
            }

            // after writing the response the server will handle disposing the stream for us.
            return new Response(request, HttpStatusCode.OK, ms) {ContentType = ResponseType};
        }