Exemplo n.º 1
0
        async public Task <IEnumerable <ProviderResult> > GenerateAllocations(BuildProject buildProject, IEnumerable <ProviderSummary> providers, Func <string, string, Task <IEnumerable <ProviderSourceDataset> > > getProviderSourceDatasets)
        {
            var assembly = Assembly.Load(buildProject.Build.Assembly);

            var allocationModel = _allocationFactory.CreateAllocationModel(assembly);

            ConcurrentBag <ProviderResult> providerResults = new ConcurrentBag <ProviderResult>();

            IEnumerable <CalculationSummaryModel> calculations = await _calculationsRepository.GetCalculationSummariesForSpecification(buildProject.SpecificationId);

            Parallel.ForEach(providers, new ParallelOptions {
                MaxDegreeOfParallelism = 5
            }, provider =>
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();

                IEnumerable <ProviderSourceDataset> providerSourceDatasets = getProviderSourceDatasets(provider.Id, buildProject.SpecificationId).Result;

                if (providerSourceDatasets == null)
                {
                    providerSourceDatasets = Enumerable.Empty <ProviderSourceDataset>();
                }

                var result = CalculateProviderResults(allocationModel, buildProject, calculations, provider, providerSourceDatasets.ToList());

                providerResults.Add(result);

                stopwatch.Stop();
                _logger.Debug($"Generated result for {provider.Name} in {stopwatch.ElapsedMilliseconds}ms");
            });

            return(providerResults);
        }
        async public Task GenerateAllocations_GivenBuildProject_Runs()
        {
            //Arrange
            BuildProject buildProject = CreateBuildProject();

            buildProject.Build.Assembly = MockData.GetMockAssembly();

            IEnumerable <ProviderSummary> providers = new[]
            {
                new ProviderSummary {
                    Id = ProviderId, Name = ProviderName
                }
            };

            IEnumerable <ProviderSourceDataset> datasets = new[]
            {
                new ProviderSourceDataset()
            };

            Func <string, string, Task <IEnumerable <ProviderSourceDataset> > > func = (s, p) =>
            {
                return(Task.FromResult(datasets));
            };

            IEnumerable <CalculationResult> calculationResults = new[]
            {
                new CalculationResult
                {
                    Calculation = new Reference {
                        Id = CalculationId
                    },
                }
            };

            IAllocationModel allocationModel = Substitute.For <IAllocationModel>();

            allocationModel
            .Execute(Arg.Any <List <ProviderSourceDataset> >(), Arg.Any <ProviderSummary>())
            .Returns(calculationResults);

            IAllocationFactory allocationFactory = Substitute.For <IAllocationFactory>();

            allocationFactory
            .CreateAllocationModel(Arg.Any <Assembly>())
            .Returns(allocationModel);

            ILogger logger = CreateLogger();

            ICalculationsRepository        calculationsRepository = CreateCalculationsRepository();
            List <CalculationSummaryModel> calculations           = new List <CalculationSummaryModel>()
            {
                new CalculationSummaryModel()
                {
                    Id = CalculationId,
                },
                new CalculationSummaryModel()
                {
                    Id = "calc2",
                },
                new CalculationSummaryModel()
                {
                    Id = "calc3",
                }
            };

            calculationsRepository
            .GetCalculationSummariesForSpecification(Arg.Any <string>())
            .Returns(calculations);

            CalculationEngine calculationEngine = CreateCalculationEngine(allocationFactory, calculationsRepository, logger: logger);

            //Act
            IEnumerable <ProviderResult> results = await calculationEngine.GenerateAllocations(buildProject, providers, func);

            //Assert
            results
            .Count()
            .Should()
            .Be(1);

            results
            .First()
            .CalculationResults
            .Count
            .Should()
            .Be(3);
        }
        public async Task PreviewCalculationResult_GivenCachedAggregateValuesExist_CalculateProviderResultsCallReceived()
        {
            IAllocationModel allocationModel = Substitute.For <IAllocationModel>();

            _calculationEngine
            .GenerateAllocationModel(Arg.Any <Assembly>())
            .Returns(allocationModel);

            ProviderVersionSearchResult providerVersionSearchResult = new ProviderVersionSearchResult
            {
                UKPRN = providerId
            };

            IEnumerable <string> dataDefinitionRelationshipIds = new List <string>();

            SpecificationSummary specificationSummary = new SpecificationSummary
            {
                DataDefinitionRelationshipIds = dataDefinitionRelationshipIds,
                ProviderVersionId             = providerVersionId
            };

            _specificationsApiClient
            .GetSpecificationSummaryById(Arg.Is(specificationId))
            .Returns(new ApiResponse <SpecificationSummary>(HttpStatusCode.OK, specificationSummary));

            _providersApiClient
            .GetProviderByIdFromProviderVersion(Arg.Is(providerVersionId), Arg.Is(providerId))
            .Returns(new ApiResponse <ProviderVersionSearchResult>(HttpStatusCode.OK, providerVersionSearchResult));

            CalculationSummaryModel previewCalculationSummaryModel = new CalculationSummaryModel();

            IEnumerable <CalculationSummaryModel> calculationSummaryModels = new List <CalculationSummaryModel>
            {
                new CalculationSummaryModel(),
                new CalculationSummaryModel()
            };

            List <CalculationSummaryModel> expectedCalculationSummaryModels = calculationSummaryModels.ToList();

            expectedCalculationSummaryModels.Add(previewCalculationSummaryModel);

            _calculationsRepository
            .GetCalculationSummariesForSpecification(Arg.Is(specificationId))
            .Returns(calculationSummaryModels);

            Dictionary <string, ProviderSourceDataset> sourceDatasets = new Dictionary <string, ProviderSourceDataset>();

            Dictionary <string, Dictionary <string, ProviderSourceDataset> > providerSourceDatasets = new Dictionary <string, Dictionary <string, ProviderSourceDataset> >
            {
                { providerId, sourceDatasets }
            };

            _providerSourceDatasetsRepository
            .GetProviderSourceDatasetsByProviderIdsAndRelationshipIds(
                Arg.Is(specificationId),
                Arg.Is <IEnumerable <string> >(_ => _ != null && _.Count() == 1 && _.FirstOrDefault() == providerId),
                Arg.Is <IEnumerable <string> >(_ => _ != null && _.SequenceEqual(dataDefinitionRelationshipIds)))
            .Returns(providerSourceDatasets);

            IEnumerable <CalculationAggregation> calculationAggregations = new List <CalculationAggregation>();

            _calculationAggregationService
            .BuildAggregations(Arg.Is <BuildAggregationRequest>(_ => _ != null && _.SpecificationId == specificationId))
            .Returns(calculationAggregations);

            PreviewCalculationRequest previewCalculationRequest = new PreviewCalculationRequest
            {
                AssemblyContent = MockData.GetMockAssembly(),
                PreviewCalculationSummaryModel = previewCalculationSummaryModel
            };

            IActionResult actionResult =
                await _calculationEnginePreviewService.PreviewCalculationResult(specificationId, providerId, previewCalculationRequest);

            _calculationEngine
            .Received(1)
            .CalculateProviderResults(
                Arg.Is(allocationModel),
                specificationId,
                Arg.Is <IEnumerable <CalculationSummaryModel> >(_ => _.SequenceEqual(expectedCalculationSummaryModels)),
                Arg.Is <ProviderSummary>(_ => _.UKPRN == providerId),
                Arg.Is <Dictionary <string, ProviderSourceDataset> >(_ => _.SequenceEqual(sourceDatasets)),
                Arg.Is <IEnumerable <CalculationAggregation> >(_ => _.SequenceEqual(calculationAggregations)));
        }
        private async Task <(IEnumerable <CalculationSummaryModel>, long)> GetCalculationSummaries(string specificationId)
        {
            Stopwatch calculationsLookupStopwatch = Stopwatch.StartNew();
            IEnumerable <CalculationSummaryModel> calculations = await _calculationsApiClientPolicy.ExecuteAsync(() =>
                                                                                                                 _calculationsRepository.GetCalculationSummariesForSpecification(specificationId));

            if (calculations == null)
            {
                _logger.Error($"Calculations lookup API returned null for specification id {specificationId}");

                throw new InvalidOperationException("Calculations lookup API returned null");
            }
            calculationsLookupStopwatch.Stop();

            return(calculations, calculationsLookupStopwatch.ElapsedMilliseconds);
        }
Exemplo n.º 5
0
        private async Task <IEnumerable <CalculationSummaryModel> > GetCalculationSummaries(string specificationId)
        {
            IEnumerable <CalculationSummaryModel> calculations = await _calculationsApiClientPolicy.ExecuteAsync(() =>
                                                                                                                 _calculationsRepository.GetCalculationSummariesForSpecification(specificationId));

            if (calculations == null)
            {
                _logger.Error($"Calculations lookup API returned null for specification id {specificationId}");

                throw new InvalidOperationException("Calculations lookup API returned null");
            }
            return(calculations);
        }
        public async Task GenerateAllocations(Message message)
        {
            Guard.ArgumentNotNull(message, nameof(message));

            _logger.Information($"Validating new allocations message");

            CalculationEngineServiceValidator.ValidateMessage(_logger, message);

            GenerateAllocationMessageProperties messageProperties = GetMessageProperties(message);

            JobViewModel job = await AddStartingProcessJobLog(messageProperties.JobId);

            if (job == null)
            {
                return;
            }

            messageProperties.GenerateCalculationAggregationsOnly = (job.JobDefinitionId == JobConstants.DefinitionNames.GenerateCalculationAggregationsJob);

            IEnumerable <ProviderSummary> summaries = null;

            _logger.Information($"Generating allocations for specification id {messageProperties.SpecificationId}");

            BuildProject buildProject = await GetBuildProject(messageProperties.SpecificationId);

            byte[] assembly = await _calculationsRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetAssemblyBySpecificationId(messageProperties.SpecificationId));

            if (assembly == null)
            {
                string error = $"Failed to get assembly for specification Id '{messageProperties.SpecificationId}'";
                _logger.Error(error);
                throw new RetriableException(error);
            }

            buildProject.Build.Assembly = assembly;

            Dictionary <string, List <decimal> > cachedCalculationAggregationsBatch = CreateCalculationAggregateBatchDictionary(messageProperties);

            _logger.Information($"processing partition index {messageProperties.PartitionIndex} for batch size {messageProperties.PartitionSize}");

            int start = messageProperties.PartitionIndex;

            int stop = start + messageProperties.PartitionSize - 1;

            summaries = await _cacheProviderPolicy.ExecuteAsync(() => _cacheProvider.ListRangeAsync <ProviderSummary>(messageProperties.ProviderCacheKey, start, stop));

            int providerBatchSize = _engineSettings.ProviderBatchSize;

            Stopwatch calculationsLookupStopwatch = Stopwatch.StartNew();
            IEnumerable <CalculationSummaryModel> calculations = await _calculationsRepositoryPolicy.ExecuteAsync(() => _calculationsRepository.GetCalculationSummariesForSpecification(messageProperties.SpecificationId));

            if (calculations == null)
            {
                _logger.Error($"Calculations lookup API returned null for specification id {messageProperties.SpecificationId}");

                throw new InvalidOperationException("Calculations lookup API returned null");
            }
            calculationsLookupStopwatch.Stop();

            IEnumerable <CalculationAggregation> aggregations = await BuildAggregations(messageProperties);

            int totalProviderResults = 0;

            bool calculationResultsHaveExceptions = false;

            for (int i = 0; i < summaries.Count(); i += providerBatchSize)
            {
                Stopwatch calculationStopwatch            = new Stopwatch();
                Stopwatch providerSourceDatasetsStopwatch = new Stopwatch();

                Stopwatch calcTiming = Stopwatch.StartNew();

                CalculationResultsModel calculationResults = await CalculateResults(summaries, calculations, aggregations, buildProject, messageProperties, providerBatchSize, i, providerSourceDatasetsStopwatch, calculationStopwatch);

                _logger.Information($"calculating results complete for specification id {messageProperties.SpecificationId}");

                long saveCosmosElapsedMs = -1;
                long saveSearchElapsedMs = -1;
                long saveRedisElapsedMs  = 0;
                long saveQueueElapsedMs  = 0;

                if (calculationResults.ProviderResults.Any())
                {
                    if (messageProperties.GenerateCalculationAggregationsOnly)
                    {
                        PopulateCachedCalculationAggregationsBatch(calculationResults.ProviderResults, cachedCalculationAggregationsBatch, messageProperties);
                    }
                    else
                    {
                        (long saveCosmosElapsedMs, long saveSearchElapsedMs, long saveRedisElapsedMs, long saveQueueElapsedMs)timingMetrics = await ProcessProviderResults(calculationResults.ProviderResults, messageProperties, message);

                        saveCosmosElapsedMs = timingMetrics.saveCosmosElapsedMs;
                        saveSearchElapsedMs = timingMetrics.saveSearchElapsedMs;
                        saveRedisElapsedMs  = timingMetrics.saveRedisElapsedMs;
                        saveQueueElapsedMs  = timingMetrics.saveQueueElapsedMs;

                        totalProviderResults += calculationResults.ProviderResults.Count();

                        if (calculationResults.ResultsContainExceptions)
                        {
                            if (!calculationResultsHaveExceptions)
                            {
                                calculationResultsHaveExceptions = true;
                            }
                        }
                    }
                }

                calcTiming.Stop();

                IDictionary <string, double> metrics = new Dictionary <string, double>()
                {
                    { "calculation-run-providersProcessed", calculationResults.PartitionedSummaries.Count() },
                    { "calculation-run-lookupCalculationDefinitionsMs", calculationsLookupStopwatch.ElapsedMilliseconds },
                    { "calculation-run-providersResultsFromCache", summaries.Count() },
                    { "calculation-run-partitionSize", messageProperties.PartitionSize },
                    { "calculation-run-providerSourceDatasetQueryMs", providerSourceDatasetsStopwatch.ElapsedMilliseconds },
                    { "calculation-run-saveProviderResultsRedisMs", saveRedisElapsedMs },
                    { "calculation-run-saveProviderResultsServiceBusMs", saveQueueElapsedMs },
                    { "calculation-run-runningCalculationMs", calculationStopwatch.ElapsedMilliseconds },
                };

                if (saveCosmosElapsedMs > -1)
                {
                    metrics.Add("calculation-run-elapsedMilliseconds", calcTiming.ElapsedMilliseconds);
                    metrics.Add("calculation-run-saveProviderResultsCosmosMs", saveCosmosElapsedMs);
                    metrics.Add("calculation-run-saveProviderResultsSearchMs", saveSearchElapsedMs);
                }
                else
                {
                    metrics.Add("calculation-run-for-tests-ms", calcTiming.ElapsedMilliseconds);
                }


                _telemetry.TrackEvent("CalculationRunProvidersProcessed",
                                      new Dictionary <string, string>()
                {
                    { "specificationId", messageProperties.SpecificationId },
                    { "buildProjectId", buildProject.Id },
                },
                                      metrics
                                      );
            }

            if (calculationResultsHaveExceptions)
            {
                await FailJob(messageProperties.JobId, totalProviderResults, "Exceptions were thrown during generation of calculation results");
            }
            else
            {
                await CompleteBatch(messageProperties, cachedCalculationAggregationsBatch, summaries.Count(), totalProviderResults);
            }
        }