From 375b950d83b2b18502bd5223dfae775dccd0d8d9 Mon Sep 17 00:00:00 2001 From: Stefan Holm Olsen Date: Sat, 2 Sep 2023 22:08:08 +0000 Subject: [PATCH] Embed audience count in first query, and remove subsequent queries. --- .../Models/AudienceSizeCachePopulator.cs | 149 ------------------ .../Models/AudienciesSelectionFactory.cs | 88 +++-------- .../Models/IAudienceSizeCachePopulator.cs | 15 -- .../GraphQL/Models/Audience.cs | 8 +- .../ServiceCollectionExtensions.cs | 1 - .../VisitorGroupInitilizationModule.cs | 1 - 6 files changed, 26 insertions(+), 236 deletions(-) delete mode 100644 src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienceSizeCachePopulator.cs delete mode 100644 src/UNRVLD.ODP.VisitorGroups/Criteria/Models/IAudienceSizeCachePopulator.cs diff --git a/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienceSizeCachePopulator.cs b/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienceSizeCachePopulator.cs deleted file mode 100644 index 558ac1b..0000000 --- a/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienceSizeCachePopulator.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using IGraphQLClient = UNRVLD.ODP.VisitorGroups.GraphQL.IGraphQLClient; -using Newtonsoft.Json; -using UNRVLD.ODP.VisitorGroups.GraphQL.Models; -using UNRVLD.ODP.VisitorGroups.GraphQL.Models.AudienceCount; -using Audience = UNRVLD.ODP.VisitorGroups.GraphQL.Models.Audience; -using EPiServer.Framework.Cache; -using System.Threading.Tasks; - -namespace UNRVLD.ODP.VisitorGroups.Criteria.Models -{ - /// - public class AudienceSizeCachePopulator : IAudienceSizeCachePopulator - { - private readonly IGraphQLClient _client; - private readonly ISynchronizedObjectInstanceCache _cache; - private readonly OdpVisitorGroupOptions _options; - private string cacheKey = "OdpVisitorGroups_AudienceList_"; - - public AudienceSizeCachePopulator(IGraphQLClient client, ISynchronizedObjectInstanceCache cache, OdpVisitorGroupOptions options) - { - _client = client; - _cache = cache; - _options = options; - } - - public void PopulateCacheItem(Audience Audience) - { - try - { - var countQuery = GetAllCountsQuery(new List { Audience }); - var countResult = _client.Query(countQuery).Result; - var countObject = JsonConvert.DeserializeObject(countResult[Audience.Name].ToString()); - - _cache.Insert(cacheKey + Audience.Name, countObject, new CacheEvictionPolicy(new TimeSpan(0, 0, _options.PopulationEstimateCacheTimeoutSeconds), CacheTimeoutType.Absolute)); - } - catch { } - } - - public async Task PopulateEntireCache(bool ForceRefresh) - { - var query = @"query MyQuery { - audiences { - edges { - node { - description - name - } - } - } - }"; - - try - { - var result = await _client.Query(query); - var orderedResult = result.Items.OrderBy(x => x.Description); - - var skip = 0; - var pageSize = 10; - while (orderedResult.Skip(skip).Take(pageSize).ToList().Count > 0) - { - var currentPageOfAudiences = orderedResult.Skip(skip).Take(pageSize).ToList(); - - // Getting the estimate is expensive so skip if we have everything in the cache already (unless we are forcing a refresh) - if (ForceRefresh == false) - { - if (CheckCacheHit(currentPageOfAudiences, pageSize)) - { - skip += pageSize; - break; - } - } - - var countQuery = GetAllCountsQuery(currentPageOfAudiences); - var countResult = _client.Query(countQuery).Result; - - var loopCount = 0; - foreach (var audienceCount in countResult) - { - var audience = orderedResult.Skip(skip + loopCount).Take(1).First(); - var countObject = JsonConvert.DeserializeObject((countResult[audience.Name].ToString())); - -#if NET5_0_OR_GREATER - _cache.Insert( - cacheKey + audience.Name, - countObject, - new CacheEvictionPolicy(new TimeSpan(0, 0, 0, _options.PopulationEstimateCacheTimeoutSeconds), CacheTimeoutType.Absolute)); -#elif NET461_OR_GREATER - // Strange behaviour in .net Framework, where it thinks the Insert method - // doesn't exist so accessing the ObjectInstanceCache directly to resolve - _cache.ObjectInstanceCache.Insert( - cacheKey + audience.Name, - countObject, - new CacheEvictionPolicy(new TimeSpan(0, 0, 0, _options.PopulationEstimateCacheTimeoutSeconds), CacheTimeoutType.Absolute)); -#endif - - loopCount++; - } - - skip += pageSize; - } - } - catch (Exception) { } - } - - private bool CheckCacheHit(IList CurrentPageOfAudiences, int PageSize) - { - var cacheHit = 0; - foreach (var cacheCheck in CurrentPageOfAudiences) - { - var cachedResult = _cache.Get(cacheKey + cacheCheck.Name); - if (cachedResult != null) - { - cacheHit++; - } - else - { - return false; - } - } - if (cacheHit == PageSize) - { - return true; - } - - return false; - } - - private string GetAllCountsQuery(List allAudiences) - { - var countQuery = $@"query MyQuery {{"; - foreach (var audience in allAudiences) - { - countQuery += $@"{audience.Name}: audience(name: ""{audience.Name}"") {{ - population_estimate(percent_error: 10) {{ - estimated_lower_bound - estimated_upper_bound - }} - }} - "; - } - countQuery += $@"}}"; - - return countQuery; - } - } -} diff --git a/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienciesSelectionFactory.cs b/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienciesSelectionFactory.cs index e66e34c..0bf9efb 100644 --- a/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienciesSelectionFactory.cs +++ b/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/AudienciesSelectionFactory.cs @@ -13,10 +13,7 @@ using IGraphQLClient = UNRVLD.ODP.VisitorGroups.GraphQL.IGraphQLClient; using EPiServer.ServiceLocation; using UNRVLD.ODP.VisitorGroups.GraphQL.Models; -using UNRVLD.ODP.VisitorGroups.GraphQL.Models.AudienceCount; using EPiServer.Framework.Cache; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; namespace UNRVLD.ODP.VisitorGroups.Criteria.Models { @@ -25,17 +22,11 @@ public class AudienciesSelectionFactory : ISelectionFactory private readonly IGraphQLClient client; private readonly ISynchronizedObjectInstanceCache cache; private string cacheKey = "OdpVisitorGroups_AudienceList_"; -#if NET5_0_OR_GREATER - private readonly IServiceScopeFactory serviceScopeFactory; -#endif public AudienciesSelectionFactory() { client = ServiceLocator.Current.GetInstance(); cache = ServiceLocator.Current.GetInstance(); -#if NET5_0_OR_GREATER - serviceScopeFactory = ServiceLocator.Current.GetInstance(); -#endif } public IEnumerable GetSelectListItems(Type propertyType) @@ -46,96 +37,55 @@ public IEnumerable GetSelectListItems(Type propertyType) node { description name + population_estimate(percent_error: 10) { + estimated_lower_bound + estimated_upper_bound + } } } } }"; - var selectItems = new List(); - try { - var cachePopulationRequested = false; var result = client.Query(query).Result; - var orderedResult = result.Items.OrderBy(x => x.Description); - - selectItems = new List(); - foreach (var audience in orderedResult) - { - var cacheResult = cache.Get(cacheKey + audience.Name); - if (cacheResult != null) - { - selectItems.Add(new SelectListItem() { Text = audience.Description + GetCountEstimateString((AudienceCount)cacheResult), Value = audience.Name }); - } - else - { - selectItems.Add(new SelectListItem() { Text = audience.Description + " (Calculating segment size...)", Value = audience.Name }); - if (cachePopulationRequested == false) - { - cachePopulationRequested = true; - -#if NET5_0_OR_GREATER - _ = Task.Run(async () => - { - try - { - using var scope = serviceScopeFactory.CreateScope(); - var cachePopulator = scope.ServiceProvider.GetRequiredService(); - await cachePopulator.PopulateEntireCache(false); - } - catch (Exception e) - { - Console.WriteLine(e); - } - }); -#elif NET461_OR_GREATER - try - { - var cachePopulator = ServiceLocator.Current.GetInstance(); - HostingEnvironment.QueueBackgroundWorkItem(c => cachePopulator.PopulateEntireCache(false)); - } - catch (Exception e) - { - Console.WriteLine(e); - } -#endif - } - } - } - return selectItems; + return result.Items + .OrderBy(x => x.Description) + .Select(audience => new SelectListItem { Text = audience.Description + GetCountEstimateString(audience), Value = audience.Name }) + .ToArray(); } - catch + catch { - return new List(); + return Enumerable.Empty(); } } - private string GetCountEstimateString(AudienceCount audienceCount) + private string GetCountEstimateString(Audience audience) { - if (audienceCount == null || - audienceCount.PopulationEstimate == null) + if (audience?.PopulationEstimate == null) { return string.Empty; } - if (audienceCount.PopulationEstimate.EstimatedLowerBound == 0) + var populationEstimate = audience.PopulationEstimate; + if (populationEstimate.EstimatedLowerBound == 0) { return " (close to 0 visitors)"; } - if (audienceCount.PopulationEstimate.EstimatedLowerBound == 1) + if (populationEstimate.EstimatedLowerBound == 1) { return " (more than 1 visitor)"; } - if (audienceCount.PopulationEstimate.EstimatedLowerBound < 100) + if (populationEstimate.EstimatedLowerBound < 100) { - return $" (more than {audienceCount.PopulationEstimate.EstimatedLowerBound} visitors)"; + return $" (more than {populationEstimate.EstimatedLowerBound} visitors)"; } - int calc = (audienceCount.PopulationEstimate.EstimatedLowerBound + - audienceCount.PopulationEstimate.EstimatedUpperBound) / 2; + int calc = (populationEstimate.EstimatedLowerBound + + populationEstimate.EstimatedUpperBound) / 2; return $" (about {calc} visitors)"; } diff --git a/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/IAudienceSizeCachePopulator.cs b/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/IAudienceSizeCachePopulator.cs deleted file mode 100644 index ae22444..0000000 --- a/src/UNRVLD.ODP.VisitorGroups/Criteria/Models/IAudienceSizeCachePopulator.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Audience = UNRVLD.ODP.VisitorGroups.GraphQL.Models.Audience; -using System.Threading.Tasks; -using EPiServer.Framework.Cache; - -namespace UNRVLD.ODP.VisitorGroups.Criteria.Models -{ - /// - /// Used to populate the cache for audience size(s) and meant for background operations - /// - public interface IAudienceSizeCachePopulator - { - Task PopulateEntireCache(bool ForceRefresh); - void PopulateCacheItem(Audience Audience); - } -} \ No newline at end of file diff --git a/src/UNRVLD.ODP.VisitorGroups/GraphQL/Models/Audience.cs b/src/UNRVLD.ODP.VisitorGroups/GraphQL/Models/Audience.cs index 1e40dc9..f3b4866 100644 --- a/src/UNRVLD.ODP.VisitorGroups/GraphQL/Models/Audience.cs +++ b/src/UNRVLD.ODP.VisitorGroups/GraphQL/Models/Audience.cs @@ -1,8 +1,14 @@ -namespace UNRVLD.ODP.VisitorGroups.GraphQL.Models +using Newtonsoft.Json; +using UNRVLD.ODP.VisitorGroups.GraphQL.Models.AudienceCount; + +namespace UNRVLD.ODP.VisitorGroups.GraphQL.Models { public class Audience { public string Name { get; set; } public string Description { get; set; } + + [JsonProperty("population_estimate")] + public PopulationEstimate PopulationEstimate { get; set; } } } \ No newline at end of file diff --git a/src/UNRVLD.ODP.VisitorGroups/Initilization/ServiceCollectionExtensions.cs b/src/UNRVLD.ODP.VisitorGroups/Initilization/ServiceCollectionExtensions.cs index 94aae27..dc5dab1 100644 --- a/src/UNRVLD.ODP.VisitorGroups/Initilization/ServiceCollectionExtensions.cs +++ b/src/UNRVLD.ODP.VisitorGroups/Initilization/ServiceCollectionExtensions.cs @@ -17,7 +17,6 @@ public static void AddODPVisitorGroups(this IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddHttpContextOrThreadScoped(); - services.AddTransient(); } } } diff --git a/src/UNRVLD.ODP.VisitorGroups/Initilization/VisitorGroupInitilizationModule.cs b/src/UNRVLD.ODP.VisitorGroups/Initilization/VisitorGroupInitilizationModule.cs index 71a85d6..899cd11 100644 --- a/src/UNRVLD.ODP.VisitorGroups/Initilization/VisitorGroupInitilizationModule.cs +++ b/src/UNRVLD.ODP.VisitorGroups/Initilization/VisitorGroupInitilizationModule.cs @@ -30,7 +30,6 @@ public void ConfigureContainer(ServiceConfigurationContext context) services.AddScoped(); services.AddScoped(); services.AddHttpContextOrThreadScoped(); - services.AddTransient(); } public void Initialize(InitializationEngine context)