From 50e0740ae2dbb9a4ca49c1536d1c6ff1b2b9c4fc Mon Sep 17 00:00:00 2001 From: Lawrence Newcombe <32834730+LawrenceLoz@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:04:49 +0000 Subject: [PATCH] Eliminate RIB query from list processing --- .../classes/FormulaShareBatchService.cls | 20 ++++-- .../default/classes/FormulaShareMetrics.cls | 6 ++ .../classes/FormulaShareMetricsSelector.cls | 5 +- .../classes/FormulaShareProcessBatch.cls | 3 + ...FormulaShareProcessedShareEventService.cls | 12 ++-- .../FormulaShareRemoveSharesSelector.cls | 4 ++ .../FormulaShareRuleInBatchSelector.cls | 3 + .../FormulaShareRulesListViewController.cls | 66 ++++++++++--------- ...ormulaShareRulesListViewControllerTest.cls | 27 ++++---- .../classes/FormulaShareTestDataFactory.cls | 16 +++++ ...ormulaShare Metrics Layout.layout-meta.xml | 8 +++ .../formulaShareRulesListView.js | 15 +++-- .../Processing_Finished__c.field-meta.xml | 10 +++ .../Processing_Started__c.field-meta.xml | 10 +++ ...ulaShare_Admin_User.permissionset-meta.xml | 10 +++ 15 files changed, 151 insertions(+), 64 deletions(-) create mode 100644 fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Finished__c.field-meta.xml create mode 100644 fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Started__c.field-meta.xml diff --git a/fs-core/main/default/classes/FormulaShareBatchService.cls b/fs-core/main/default/classes/FormulaShareBatchService.cls index 4126a3a..ca8daf6 100644 --- a/fs-core/main/default/classes/FormulaShareBatchService.cls +++ b/fs-core/main/default/classes/FormulaShareBatchService.cls @@ -76,8 +76,10 @@ public virtual inherited sharing class FormulaShareBatchService { } // Create metrics for the execute context for any rules which don't have them already - List metrics = createMetricsForNewRules(); - uow.registerNew(metrics); + // Note that 'n/a' indicates a query wasn't run and a batch start error was encountered (logBatchStartError will be run) + if(query != 'n/a') { + setProcessingStartedAndCreateMetricsForNewRules(uow); + } uow.commitWork(); } @@ -396,7 +398,8 @@ public virtual inherited sharing class FormulaShareBatchService { } - private List createMetricsForNewRules() { + private void setProcessingStartedAndCreateMetricsForNewRules(fflib_ISObjectUnitOfWork uow) { + Datetime timeNow = Datetime.now(); // Make a set of rules which haven't had metrics records created already Set ruleNames = new Set(); @@ -406,13 +409,18 @@ public virtual inherited sharing class FormulaShareBatchService { FormulaShareMetricsSelector fsmSelector = new FormulaShareMetricsSelector(); List existingMetrics = fsmSelector.getMetricsForRulesAndContext(ruleNames, executeContext); for(FormulaShare_Metrics__c m : existingMetrics) { + m.Processing_Started__c = timeNow; + m.Processing_Finished__c = null; // Clear previous finish time ruleNames.remove(m.FormulaShare_Rule_Name__c); // Take rules with metrics out of the set } + uow.registerDirty(existingMetrics); // Get list of blank metrics records for all rules which don't yet have batch metrics - List allMetrics = FormulaShareMetrics.getBlankMetrics(ruleNames, executeContext); - - return allMetrics; + List newMetrics = FormulaShareMetrics.getBlankMetrics(ruleNames, executeContext); + for(FormulaShare_Metrics__c metrics : newMetrics) { + metrics.Processing_Started__c = timeNow; + } + uow.registerNew(newMetrics); } } diff --git a/fs-core/main/default/classes/FormulaShareMetrics.cls b/fs-core/main/default/classes/FormulaShareMetrics.cls index 6bb005c..8fdfa49 100644 --- a/fs-core/main/default/classes/FormulaShareMetrics.cls +++ b/fs-core/main/default/classes/FormulaShareMetrics.cls @@ -29,6 +29,7 @@ public inherited sharing class FormulaShareMetrics extends fflib_SObjectDomain { public DateTime lastFailedOperation; public DateTime LastSuccessfulBatch; public DateTime lastFailedBatch; + public DateTime processingFinished; } public FormulaShareMetrics(List sObjectList) { @@ -110,6 +111,11 @@ public inherited sharing class FormulaShareMetrics extends fflib_SObjectDomain { metrics.Last_Successful_Batch__c = inc.LastSuccessfulBatch; metrics.Last_Failed_Batch__c = inc.lastFailedBatch; + // Override Processing Finished if explicitly provided + if(inc.processingFinished != null) { + metrics.Processing_Finished__c = inc.processingFinished; + } + if(metrics.Id == null) { uow.registerNew(metrics); } diff --git a/fs-core/main/default/classes/FormulaShareMetricsSelector.cls b/fs-core/main/default/classes/FormulaShareMetricsSelector.cls index 97b4971..0f4446c 100644 --- a/fs-core/main/default/classes/FormulaShareMetricsSelector.cls +++ b/fs-core/main/default/classes/FormulaShareMetricsSelector.cls @@ -49,7 +49,10 @@ public inherited sharing class FormulaShareMetricsSelector extends fflib_SObject FormulaShare_Metrics__c.Successful_Inserts__c, FormulaShare_Metrics__c.Context__c, FormulaShare_Metrics__c.FormulaShare_Rule_Name__c, - FormulaShare_Metrics__c.Last_Batch_Run__c + FormulaShare_Metrics__c.Last_Batch_Run__c, + FormulaShare_Metrics__c.Processing_Started__c, + FormulaShare_Metrics__c.Processing_Finished__c, + FormulaShare_Metrics__c.LastModifiedDate }; } diff --git a/fs-core/main/default/classes/FormulaShareProcessBatch.cls b/fs-core/main/default/classes/FormulaShareProcessBatch.cls index 9f8aa87..c8694b5 100644 --- a/fs-core/main/default/classes/FormulaShareProcessBatch.cls +++ b/fs-core/main/default/classes/FormulaShareProcessBatch.cls @@ -83,6 +83,9 @@ public class FormulaShareProcessBatch implements Database.Batchable, Da this.batchService.createBatchAndRibLogs('n/a', this.batchSize); this.batchService.logBatchStartError(this.batchStartError); + + // Update list to clear "Processing..." label + EventBus.publish(new FormulaShare_List_Update__e()); } return ql; diff --git a/fs-core/main/default/classes/FormulaShareProcessedShareEventService.cls b/fs-core/main/default/classes/FormulaShareProcessedShareEventService.cls index 01dd50c..9e54562 100644 --- a/fs-core/main/default/classes/FormulaShareProcessedShareEventService.cls +++ b/fs-core/main/default/classes/FormulaShareProcessedShareEventService.cls @@ -21,11 +21,6 @@ public inherited sharing class FormulaShareProcessedShareEventService { - static String context; // "Batch" and "Trigger" supported - static FormulaShare_Log__c batchLog; - static FormulaShare_Settings__mdt settings; - static fflib_ISObjectUnitOfWork uow; - // Metrics type names defined below. // Typically we would expect each rule to have at most one metrics record for the following: // @@ -43,6 +38,11 @@ public inherited sharing class FormulaShareProcessedShareEventService { public static final String triggerContext = 'Trigger Operations Since Last Batch'; public static final String batchFinishContext = 'Last Batch'; + static String context; + static FormulaShare_Log__c batchLog; + static FormulaShare_Settings__mdt settings; + static fflib_ISObjectUnitOfWork uow; + private static Integer noRecordsSharingAppliedFirstTime; private static Integer noRecordsAllSharingRemoved; private static Integer noRecordsSharingUpdated; @@ -225,10 +225,12 @@ public inherited sharing class FormulaShareProcessedShareEventService { // Create a zero metric for each rule Map metricsIncrements = new Map(); + Datetime timeNow = Datetime.now(); for(String ruleName : ruleNamesSet) { FormulaShareMetrics.Increment inc = new FormulaShareMetrics.Increment(); inc.noSuccessfulInserts = 0; inc.noFailedInserts = 0; + inc.processingFinished = timeNow; metricsIncrements.put(ruleName, inc); } diff --git a/fs-core/main/default/classes/FormulaShareRemoveSharesSelector.cls b/fs-core/main/default/classes/FormulaShareRemoveSharesSelector.cls index 4ca9330..ce9546c 100644 --- a/fs-core/main/default/classes/FormulaShareRemoveSharesSelector.cls +++ b/fs-core/main/default/classes/FormulaShareRemoveSharesSelector.cls @@ -60,4 +60,8 @@ public virtual inherited sharing class FormulaShareRemoveSharesSelector { LIMIT 1]; } + public virtual Set getObjectsWhereFullBatchUnsupported() { + return new Set(); + } + } diff --git a/fs-core/main/default/classes/FormulaShareRuleInBatchSelector.cls b/fs-core/main/default/classes/FormulaShareRuleInBatchSelector.cls index 816fe2a..cd4652f 100644 --- a/fs-core/main/default/classes/FormulaShareRuleInBatchSelector.cls +++ b/fs-core/main/default/classes/FormulaShareRuleInBatchSelector.cls @@ -21,6 +21,8 @@ public inherited sharing class FormulaShareRuleInBatchSelector extends fflib_SObjectSelector { + private final Integer QUERY_LIMIT = 10000; + public List fields = new List(); public FormulaShareRuleInBatchSelector(Boolean enforceObjectAndFieldSecurity) { @@ -50,6 +52,7 @@ public inherited sharing class FormulaShareRuleInBatchSelector extends fflib_SOb selectField('FormulaShare_Log__r.Processing_Finished__c'). selectField('FormulaShare_Log__r.Batch_Finish_Complete__c'). setCondition('FormulaShare_Rule_Name__c IN :ruleNames'). + setLimit(QUERY_LIMIT). toSOQL()); } diff --git a/fs-core/main/default/classes/FormulaShareRulesListViewController.cls b/fs-core/main/default/classes/FormulaShareRulesListViewController.cls index b149eef..144e70a 100644 --- a/fs-core/main/default/classes/FormulaShareRulesListViewController.cls +++ b/fs-core/main/default/classes/FormulaShareRulesListViewController.cls @@ -37,33 +37,6 @@ public with sharing class FormulaShareRulesListViewController { ruleNames.add(rule.developerName); } - // Assess when last sharing calculation happened - FormulaShareRuleInBatchSelector ribSelector = new FormulaShareRuleInBatchSelector(true); - List runDetails = ribSelector.getRuleRuns(ruleNames); - Map ruleRunDetailMap = new Map(); - - // Iterate through logs and build map of latest calculations - for(FormulaShareRuleInBatchSelector.RuleRunDetail runDetail : runDetails) { - - // Consider only if rule was active when batch ran - if(runDetail.ruleActive) { - - if(ruleRunDetailMap.containsKey(runDetail.developerName)) { - FormulaShareRuleInBatchSelector.RuleRunDetail thisDetail = ruleRunDetailMap.get(runDetail.developerName); - - // If log is later than current latest, replace this (otherwise we'll keep the later log in the map) - if(thisDetail.processingStarted < runDetail.processingStarted) { - ruleRunDetailMap.put(runDetail.developerName, runDetail); - } - } - - // If there wasn't already times in the map, record the details from this log - else { - ruleRunDetailMap.put(runDetail.developerName, runDetail); - } - } - } - // Build map of all metrics for rules List metricsList = FormulaShareMetricsSelector.construct().getAllMetricsForRules(new Set(ruleNames)); Map metricsMap = new Map(); @@ -154,16 +127,32 @@ public with sharing class FormulaShareRulesListViewController { } // Set calculation status depending on most recent logs - if(!ruleRunDetailMap.containsKey(rw.developerName)) { + FormulaShare_Metrics__c batchMetrics = metricsMap.get(rw.developerName + FormulaShareProcessedShareEventService.batchContext); + FormulaShare_Metrics__c targetedJobMetrics = metricsMap.get(rw.developerName + FormulaShareProcessedShareEventService.targetedJobContext); + + if(batchMetrics == null && targetedJobMetrics == null) { rw.lastCalcStatus = 'Pending'; rw.iconName = 'standard:today'; rw.iconAlt = 'Pending'; - } + } else { - FormulaShareRuleInBatchSelector.RuleRunDetail runDetail = ruleRunDetailMap.get(rw.developerName); - if(runDetail.batchFinishComplete) { - rw.lastCalcStatus = runDetail.processingFinished.format(); + FormulaShare_Metrics__c latestMetrics; + if(batchMetrics == null) { + latestMetrics = targetedJobMetrics; + } + else if(targetedJobMetrics == null) { + latestMetrics = batchMetrics; + } + else if(targetedJobMetrics.LastModifiedDate > batchMetrics.LastModifiedDate) { + latestMetrics = targetedJobMetrics; + } + else { + latestMetrics = batchMetrics; + } + + if(latestMetrics.Processing_Finished__c != null) { + rw.lastCalcStatus = latestMetrics.Processing_Finished__c.format(); } // If currently processing, indicate this against the field @@ -184,8 +173,17 @@ public with sharing class FormulaShareRulesListViewController { // Construct list of object wrappers with rules to return List orwList = new List(); + + Datetime timeNow = DateTime.now(); + + // Get object which can only be calculated by targeted jobs (these don't allow Recalculate Sharing option) + FormulaShareRemoveSharesSelector selector = new FormulaShareInjectionService().getRemoveSharesSelector(); + Set objectsNotSupportingRecalculate = selector.getObjectsWhereFullBatchUnsupported(); + for(String objectName : objectsWithRulesMap.keySet()) { ObjectRulesWrapper orw = new ObjectRulesWrapper(); + orw.refreshTime = timeNow; + orw.recalculateNotSupported = objectsNotSupportingRecalculate.contains(objectName); List rwList = objectsWithRulesMap.get(objectName); rwList.sort(); for(RuleWrapper rw : rwList) { @@ -242,6 +240,10 @@ public with sharing class FormulaShareRulesListViewController { public List items {get;set;} @AuraEnabled public String sharedObjectClass {get;set;} + @AuraEnabled + public Datetime refreshTime {get;set;} + @AuraEnabled + public Boolean recalculateNotSupported {get;set;} // Comparable interface allows us to call sort public Integer compareTo(Object compareTo) { diff --git a/fs-core/main/default/classes/FormulaShareRulesListViewControllerTest.cls b/fs-core/main/default/classes/FormulaShareRulesListViewControllerTest.cls index 9863eee..99d3020 100644 --- a/fs-core/main/default/classes/FormulaShareRulesListViewControllerTest.cls +++ b/fs-core/main/default/classes/FormulaShareRulesListViewControllerTest.cls @@ -90,11 +90,9 @@ public with sharing class FormulaShareRulesListViewControllerTest { log.Processing_Started_Milliseconds__c = DateTime.now().getTime(); insert log; - FormulaShare_Rule_in_Batch__c rib = new FormulaShare_Rule_in_Batch__c(); - rib.FormulaShare_Log__c = log.Id; - rib.FormulaShare_Rule_Name__c = fsRules[0].developerName; - rib.Rule_Active_for_Batch_Run__c = true; - insert rib; + FormulaShare_Metrics__c m1 = FormulaShareTestDataFactory.getBatchMetricsForRuleAndBatch(fsRules[0].developerName, log.Id); + m1.Processing_Started__c = DateTime.now().addMinutes(-5); + insert m1; // Run core method List orwList = FormulaShareRulesListViewController.getTreeGridData(); @@ -135,7 +133,6 @@ public with sharing class FormulaShareRulesListViewControllerTest { setMocks(); List fsRules = FormulaShareRulesSelector.construct().getAllRulesWithSecurityEnforced(); - // Insert two logs and RIB records for the first rule indicating processing not yet complete DateTime now = DateTime.now(); FormulaShare_Log__c log = new FormulaShare_Log__c(); @@ -149,16 +146,14 @@ public with sharing class FormulaShareRulesListViewControllerTest { insert log; insert log2; - FormulaShare_Rule_in_Batch__c rib = new FormulaShare_Rule_in_Batch__c(); - FormulaShare_Rule_in_Batch__c rib2 = new FormulaShare_Rule_in_Batch__c(); - rib.FormulaShare_Log__c = log.Id; - rib.FormulaShare_Rule_Name__c = fsRules[0].developerName; - rib.Rule_Active_for_Batch_Run__c = true; - rib2.FormulaShare_Log__c = log2.Id; - rib2.FormulaShare_Rule_Name__c = fsRules[0].developerName; - rib2.Rule_Active_for_Batch_Run__c = true; - insert rib; - insert rib2; + FormulaShare_Metrics__c m1 = FormulaShareTestDataFactory.getBatchMetricsForRuleAndBatch(fsRules[0].developerName, log.Id); + FormulaShare_Metrics__c m2 = FormulaShareTestDataFactory.getTargetedJobMetricsForRuleAndBatch(fsRules[0].developerName, log2.Id); + m1.Processing_Started__c = now.addDays(-1); + m1.Processing_Finished__c = now; + m2.Processing_Started__c = now.addDays(-2); + m2.Processing_Finished__c = now.addDays(-1); + insert m1; + insert m2; // Run core method List orwList = FormulaShareRulesListViewController.getTreeGridData(); diff --git a/fs-core/main/default/classes/FormulaShareTestDataFactory.cls b/fs-core/main/default/classes/FormulaShareTestDataFactory.cls index 5512519..6effd31 100644 --- a/fs-core/main/default/classes/FormulaShareTestDataFactory.cls +++ b/fs-core/main/default/classes/FormulaShareTestDataFactory.cls @@ -3,6 +3,7 @@ public inherited sharing class FormulaShareTestDataFactory { static String batchContext = FormulaShareProcessedShareEventService.batchContext; static String triggerContext = FormulaShareProcessedShareEventService.triggerContext; + static String targetedJobContext = FormulaShareProcessedShareEventService.targetedJobContext; static String batchFinishContext = FormulaShareProcessedShareEventService.batchFinishContext; static String userRuleName = FormulaShareRuleFactory.userRuleName; @@ -164,6 +165,7 @@ public inherited sharing class FormulaShareTestDataFactory { metrics.Failed_Inserts__c = 2; metrics.Last_Successful_Operation__c = DateTime.now(); metrics.Last_Failed_Operation__c = DateTime.now(); + metrics.Processing_Started__c = DateTime.now(); metrics.Last_Batch_Run__c = batchLogId; return metrics; } @@ -176,6 +178,20 @@ public inherited sharing class FormulaShareTestDataFactory { metrics.Failed_Inserts__c = 3; metrics.Last_Successful_Operation__c = DateTime.now(); metrics.Last_Failed_Operation__c = DateTime.now(); + metrics.Processing_Started__c = DateTime.now(); + metrics.Last_Batch_Run__c = batchLogId; + return metrics; + } + + public static FormulaShare_Metrics__c getTargetedJobMetricsForRuleAndBatch(String ruleName, Id batchLogId) { + FormulaShare_Metrics__c metrics = new FormulaShare_Metrics__c(); + metrics.FormulaShare_Rule_Name__c = ruleName; + metrics.Context__c = targetedJobContext; + metrics.Successful_Inserts__c = 104; + metrics.Failed_Inserts__c = 4; + metrics.Last_Successful_Operation__c = DateTime.now(); + metrics.Last_Failed_Operation__c = DateTime.now(); + metrics.Processing_Started__c = DateTime.now(); metrics.Last_Batch_Run__c = batchLogId; return metrics; } diff --git a/fs-core/main/default/layouts/FormulaShare_Metrics__c-FormulaShare Metrics Layout.layout-meta.xml b/fs-core/main/default/layouts/FormulaShare_Metrics__c-FormulaShare Metrics Layout.layout-meta.xml index 4160bd5..06e7e33 100644 --- a/fs-core/main/default/layouts/FormulaShare_Metrics__c-FormulaShare Metrics Layout.layout-meta.xml +++ b/fs-core/main/default/layouts/FormulaShare_Metrics__c-FormulaShare Metrics Layout.layout-meta.xml @@ -30,6 +30,14 @@ Edit Last_Batch_Run__c + + Edit + Processing_Started__c + + + Edit + Processing_Finished__c + diff --git a/fs-core/main/default/lwc/formulaShareRulesListView/formulaShareRulesListView.js b/fs-core/main/default/lwc/formulaShareRulesListView/formulaShareRulesListView.js index 8a91a52..7616731 100644 --- a/fs-core/main/default/lwc/formulaShareRulesListView/formulaShareRulesListView.js +++ b/fs-core/main/default/lwc/formulaShareRulesListView/formulaShareRulesListView.js @@ -50,7 +50,7 @@ export default class TreeGrid extends NavigationMixin(LightningElement) { this.columns.push( {type: 'text' , fieldName: 'lastCalcStatus' - , label: 'Last Full Assessment' + , label: 'Last Batch Assessment' , cellAttributes: {iconName: {fieldName: 'iconName'}, iconAlternativeText: {fieldName: 'iconAlt'} } }, {type: 'number' @@ -142,6 +142,7 @@ export default class TreeGrid extends NavigationMixin(LightningElement) { if (data) { let tempjson = JSON.parse(JSON.stringify(data).split('items').join('_children')); this.treeItems = tempjson; +// console.log('Refresh During: '+JSON.stringify(this.treeItems, null, 2)); versionSupportsRelatedRules() .then((supportsRelated) => { @@ -225,7 +226,7 @@ export default class TreeGrid extends NavigationMixin(LightningElement) { // Subscribe to list update events (raised by batch job and on rule activate/deactivate) const listUpdateCallback = (response) => { - //console.log('Received Refresh Event'); + console.log('Received Refresh Event'); this.refreshView(); }; subscribe('/event/'+prefix+'FormulaShare_List_Update__e', -1, listUpdateCallback).then(response => { @@ -278,7 +279,8 @@ export default class TreeGrid extends NavigationMixin(LightningElement) { if(isParentRow) { actions.push({ 'label': 'Recalculate Sharing', - 'name': 'recalculate' + 'name': 'recalculate', + disabled: row['recalculateNotSupported'] }); } else { @@ -420,7 +422,12 @@ export default class TreeGrid extends NavigationMixin(LightningElement) { // Refreshes provisioned list of rules refreshView() { - refreshApex(this.provisionedValue); +// console.log('Refresh Before: '+JSON.stringify(this.provisionedValue, null, 2)); + refreshApex(this.provisionedValue) + .then(() => { +// console.log('Refresh After: '+JSON.stringify(this.provisionedValue, null, 2)); + }); + const evt = new CustomEvent('refreshview'); this.dispatchEvent(evt); } diff --git a/fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Finished__c.field-meta.xml b/fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Finished__c.field-meta.xml new file mode 100644 index 0000000..ca734c7 --- /dev/null +++ b/fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Finished__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Processing_Finished__c + Only set for batch and targeted job metrics (not batch finish metrics) - drives indication of most recent processing on list view + false + + false + false + DateTime + diff --git a/fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Started__c.field-meta.xml b/fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Started__c.field-meta.xml new file mode 100644 index 0000000..ebc1548 --- /dev/null +++ b/fs-core/main/default/objects/FormulaShare_Metrics__c/fields/Processing_Started__c.field-meta.xml @@ -0,0 +1,10 @@ + + + Processing_Started__c + Only set for trigger, batch and targeted job metrics (not batch finish metrics) + false + + false + false + DateTime + diff --git a/fs-core/main/os/default/permissionsets/FormulaShare_Admin_User.permissionset-meta.xml b/fs-core/main/os/default/permissionsets/FormulaShare_Admin_User.permissionset-meta.xml index 359acf1..7c199e4 100644 --- a/fs-core/main/os/default/permissionsets/FormulaShare_Admin_User.permissionset-meta.xml +++ b/fs-core/main/os/default/permissionsets/FormulaShare_Admin_User.permissionset-meta.xml @@ -509,6 +509,16 @@ FormulaShare_Metrics__c.Object__c true + + true + FormulaShare_Metrics__c.Processing_Finished__c + true + + + true + FormulaShare_Metrics__c.Processing_Started__c + true + true FormulaShare_Metrics__c.Successful_Inserts__c