UNPKG

sf-agent-framework

Version:

AI Agent Orchestration Framework for Salesforce Development - Two-phase architecture with 70% context reduction

639 lines (524 loc) 19.1 kB
# Performance Optimization Task ## Task Overview **Objective:** Analyze and optimize Salesforce org performance, addressing bottlenecks in queries, UI rendering, integrations, and system resources. **When to Use:** - Users report slow page loads - Governor limit exceptions occur - Before major data loads - After significant customizations - Regular performance audits - Scaling for growth ## Prerequisites - System Administrator access - Performance baseline metrics - Access to debug logs - Understanding of governor limits - Query plan access - Performance monitoring tools ## Task Steps ### Step 1: Establish Performance Baseline ```apex public class PerformanceBaseline { public static void captureMetrics() { Map<String, Object> metrics = new Map<String, Object>(); // Capture current limits metrics.put('HeapSizeUsed', Limits.getHeapSize()); metrics.put('QueriesUsed', Limits.getQueries()); metrics.put('QueryRowsUsed', Limits.getQueryRows()); metrics.put('DMLRowsUsed', Limits.getDmlRows()); metrics.put('CPUTimeUsed', Limits.getCpuTime()); // Log baseline System.debug('Performance Baseline: ' + JSON.serialize(metrics)); // Store for comparison Performance_Metric__c metric = new Performance_Metric__c( Metric_Date__c = DateTime.now(), Heap_Size__c = Limits.getHeapSize(), Queries__c = Limits.getQueries(), Query_Rows__c = Limits.getQueryRows(), DML_Rows__c = Limits.getDmlRows(), CPU_Time__c = Limits.getCpuTime() ); insert metric; } } ``` ### Step 2: Analyze SOQL Query Performance ```apex public class QueryOptimizer { public static void analyzeQueries() { // Enable debug logs for query analysis TraceFlag tf = new TraceFlag(); tf.TracedEntityId = UserInfo.getUserId(); tf.LogType = 'USER_DEBUG'; tf.StartDate = DateTime.now(); tf.ExpirationDate = DateTime.now().addHours(1); insert tf; // Sample query analysis Long startTime = DateTime.now().getTime(); // Bad query example - non-selective List<Account> accounts = [ SELECT Id, Name, (SELECT Id, Name FROM Contacts), (SELECT Id, Amount FROM Opportunities) FROM Account WHERE CreatedDate != null ]; Long endTime = DateTime.now().getTime(); System.debug('Query Time: ' + (endTime - startTime) + 'ms'); // Optimized query - selective with indexes startTime = DateTime.now().getTime(); Set<Id> accountIds = new Map<Id, Account>([ SELECT Id FROM Account WHERE Industry = 'Technology' AND AnnualRevenue > 1000000 LIMIT 100 ]).keySet(); List<Account> optimizedAccounts = [ SELECT Id, Name FROM Account WHERE Id IN :accountIds ]; List<Contact> contacts = [ SELECT Id, Name, AccountId FROM Contact WHERE AccountId IN :accountIds ]; List<Opportunity> opportunities = [ SELECT Id, Amount, AccountId FROM Opportunity WHERE AccountId IN :accountIds ]; endTime = DateTime.now().getTime(); System.debug('Optimized Query Time: ' + (endTime - startTime) + 'ms'); } public static List<String> getSlowQueries() { List<String> slowQueries = new List<String>(); // Parse debug logs for slow queries List<ApexLog> logs = [ SELECT Id, LogUser.Name, StartTime, DurationMilliseconds, Operation FROM ApexLog WHERE StartTime >= DateTime.now().addHours(-24) AND DurationMilliseconds > 1000 ORDER BY DurationMilliseconds DESC LIMIT 10 ]; for (ApexLog log : logs) { slowQueries.add('User: ' + log.LogUser.Name + ', Duration: ' + log.DurationMilliseconds + 'ms' + ', Operation: ' + log.Operation); } return slowQueries; } } ``` ### Step 3: Optimize Apex Code Performance ```apex public class ApexOptimizer { // Before optimization - inefficient code public static void inefficientMethod() { List<Account> accounts = [SELECT Id FROM Account]; for (Account acc : accounts) { // SOQL in loop - BAD List<Contact> contacts = [ SELECT Id FROM Contact WHERE AccountId = :acc.Id ]; for (Contact con : contacts) { // DML in nested loop - VERY BAD con.Description = 'Updated'; update con; } } } // After optimization - bulkified code public static void optimizedMethod() { // Single query with relationship Map<Id, Account> accountMap = new Map<Id, Account>([ SELECT Id, (SELECT Id, Description FROM Contacts) FROM Account ]); List<Contact> contactsToUpdate = new List<Contact>(); // Process in memory for (Account acc : accountMap.values()) { for (Contact con : acc.Contacts) { con.Description = 'Updated'; contactsToUpdate.add(con); } } // Single DML operation if (!contactsToUpdate.isEmpty()) { update contactsToUpdate; } } // Collection optimization public static void optimizeCollections() { // Use Maps for lookups instead of nested loops Map<Id, Account> accountMap = new Map<Id, Account>( [SELECT Id, Name FROM Account] ); List<Opportunity> opps = [ SELECT Id, AccountId FROM Opportunity ]; // Efficient lookup - O(1) instead of O(n) for (Opportunity opp : opps) { Account relatedAccount = accountMap.get(opp.AccountId); // Process... } } } ``` ### Step 4: Lightning Component Performance ```javascript // Optimize Lightning Web Components export default class OptimizedComponent extends LightningElement { // Cache expensive computations _processedData; @wire(getAccounts) wiredAccounts({ data, error }) { if (data) { // Process data once and cache if (!this._processedData) { this._processedData = this.processData(data); } } } processData(data) { // Expensive processing return data.map((account) => ({ ...account, // Complex calculations revenue: this.calculateRevenue(account), score: this.calculateScore(account), })); } // Use getter for reactive properties get filteredAccounts() { if (!this._processedData) return []; // Filter cached data instead of re-processing return this._processedData.filter((account) => account.revenue > this.minRevenue); } // Debounce expensive operations handleSearchChange(event) { window.clearTimeout(this.delayTimeout); const searchKey = event.target.value; // Delay search execution this.delayTimeout = setTimeout(() => { this.searchAccounts(searchKey); }, 300); } // Lazy load components async loadHeavyComponent() { if (!this.heavyComponent) { const module = await import('c/heavyComponent'); this.heavyComponent = module.default; } } } ``` ### Step 5: Optimize Visualforce Pages ```xml <!-- Before optimization --> <apex:page controller="AccountController"> <apex:form> <apex:pageBlock> <apex:pageBlockTable value="{!accounts}" var="acc"> <!-- Inefficient - causes view state issues --> <apex:column value="{!acc.Name}"/> <apex:column value="{!acc.Contacts}"/> <apex:column value="{!acc.Opportunities}"/> </apex:pageBlockTable> </apex:pageBlock> </apex:form> </apex:page> <!-- After optimization --> <apex:page controller="AccountController" readOnly="true" cache="true"> <!-- Use JavaScript remoting for better performance --> <script> Visualforce.remoting.Manager.invokeAction( '{!$RemoteAction.AccountController.getAccountData}', function(result, event) { if (event.status) { renderAccountTable(result); } }, {escape: false, buffer: false} ); function renderAccountTable(data) { // Client-side rendering for better performance const table = document.getElementById('accountTable'); data.forEach(account => { const row = table.insertRow(); row.insertCell(0).textContent = account.Name; row.insertCell(1).textContent = account.ContactCount; row.insertCell(2).textContent = account.OpportunityTotal; }); } </script> <div class="slds"> <table id="accountTable" class="slds-table"> <thead> <tr> <th>Account Name</th> <th>Contacts</th> <th>Opportunity Total</th> </tr> </thead> <tbody></tbody> </table> </div> </apex:page> ``` ### Step 6: Database and Storage Optimization ```apex public class DatabaseOptimizer { public static void optimizeDataModel() { // Identify large objects List<EntityDefinition> largeObjects = [ SELECT QualifiedApiName, Count FROM EntityDefinition WHERE Count > 1000000 ORDER BY Count DESC ]; for (EntityDefinition ed : largeObjects) { System.debug('Large object: ' + ed.QualifiedApiName + ' Records: ' + ed.Count); // Recommend archival strategy if (ed.Count > 10000000) { System.debug('Consider Big Objects or external storage'); } } } public static void implementArchivalStrategy() { // Move old records to Big Objects List<Case> oldCases = [ SELECT Id, CaseNumber, Subject, Description, CreatedDate FROM Case WHERE ClosedDate < LAST_N_YEARS:2 AND Status = 'Closed' LIMIT 10000 ]; List<Case_Archive__b> archives = new List<Case_Archive__b>(); for (Case c : oldCases) { Case_Archive__b archive = new Case_Archive__b(); archive.Case_Number__c = c.CaseNumber; archive.Subject__c = c.Subject; archive.Description__c = c.Description; archive.Created_Date__c = c.CreatedDate; archives.add(archive); } // Bulk insert to Big Object if (!archives.isEmpty()) { Database.insertImmediate(archives); // Delete archived records delete oldCases; } } } ``` ### Step 7: Integration Performance Optimization ```apex public class IntegrationOptimizer { // Implement caching for external calls private static Map<String, Object> cache = new Map<String, Object>(); private static Map<String, DateTime> cacheExpiry = new Map<String, DateTime>(); public static Object getCachedData(String key) { if (cache.containsKey(key)) { DateTime expiry = cacheExpiry.get(key); if (expiry > DateTime.now()) { return cache.get(key); } } return null; } public static void setCachedData(String key, Object data, Integer ttlMinutes) { cache.put(key, data); cacheExpiry.put(key, DateTime.now().addMinutes(ttlMinutes)); } // Batch API calls public static void optimizeBulkCallouts() { List<Account> accounts = [ SELECT Id, External_Id__c FROM Account WHERE External_Id__c = null LIMIT 100 ]; // Batch requests Http http = new Http(); HttpRequest request = new HttpRequest(); request.setEndpoint('https://api.example.com/bulk'); request.setMethod('POST'); // Create bulk payload Map<String, Object> bulkPayload = new Map<String, Object>(); bulkPayload.put('records', accounts); request.setBody(JSON.serialize(bulkPayload)); HttpResponse response = http.send(request); // Process bulk response if (response.getStatusCode() == 200) { Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody()); processBulkResults(results); } } } ``` ### Step 8: Implement Performance Monitoring ```apex public class PerformanceMonitor { public static void monitorTransactionPerformance() { Performance_Log__c log = new Performance_Log__c(); log.Transaction_Start__c = DateTime.now(); log.User__c = UserInfo.getUserId(); // Capture initial limits log.Initial_Queries__c = Limits.getQueries(); log.Initial_Query_Rows__c = Limits.getQueryRows(); log.Initial_DML_Rows__c = Limits.getDmlRows(); log.Initial_CPU_Time__c = Limits.getCpuTime(); // Execute monitored code try { // Your business logic here executeBusinessLogic(); // Capture final limits log.Final_Queries__c = Limits.getQueries(); log.Final_Query_Rows__c = Limits.getQueryRows(); log.Final_DML_Rows__c = Limits.getDmlRows(); log.Final_CPU_Time__c = Limits.getCpuTime(); log.Status__c = 'Success'; } catch (Exception e) { log.Status__c = 'Error'; log.Error_Message__c = e.getMessage(); } log.Transaction_End__c = DateTime.now(); log.Duration_ms__c = log.Transaction_End__c.getTime() - log.Transaction_Start__c.getTime(); insert log; } @future public static void analyzePerformanceTrends() { // Analyze performance over time AggregateResult[] results = [ SELECT DAY_ONLY(Transaction_Start__c) day, AVG(Duration_ms__c) avgDuration, MAX(Duration_ms__c) maxDuration, AVG(Final_CPU_Time__c) avgCPU, COUNT(Id) transactionCount FROM Performance_Log__c WHERE Transaction_Start__c = LAST_N_DAYS:7 GROUP BY DAY_ONLY(Transaction_Start__c) ]; for (AggregateResult ar : results) { System.debug('Date: ' + ar.get('day') + ', Avg Duration: ' + ar.get('avgDuration') + ', Max Duration: ' + ar.get('maxDuration')); } } } ``` ### Step 9: Create Performance Dashboard ```javascript // Lightning Web Component for Performance Dashboard import { LightningElement, wire } from 'lwc'; import getPerformanceMetrics from '@salesforce/apex/PerformanceController.getPerformanceMetrics'; import { loadScript } from 'lightning/platformResourceLoader'; import chartJs from '@salesforce/resourceUrl/chartJs'; export default class PerformanceDashboard extends LightningElement { chartInitialized = false; @wire(getPerformanceMetrics) wiredMetrics({ data, error }) { if (data) { this.renderCharts(data); } } async renderCharts(metrics) { if (!this.chartInitialized) { await loadScript(this, chartJs); this.chartInitialized = true; } // Response Time Chart const responseCtx = this.template.querySelector('.response-chart').getContext('2d'); new Chart(responseCtx, { type: 'line', data: { labels: metrics.dates, datasets: [ { label: 'Average Response Time (ms)', data: metrics.responseTimes, borderColor: 'rgb(75, 192, 192)', tension: 0.1, }, ], }, }); // Governor Limits Chart const limitsCtx = this.template.querySelector('.limits-chart').getContext('2d'); new Chart(limitsCtx, { type: 'radar', data: { labels: ['SOQL', 'DML', 'CPU', 'Heap', 'Callouts'], datasets: [ { label: 'Current Usage %', data: metrics.limitUsage, backgroundColor: 'rgba(255, 99, 132, 0.2)', borderColor: 'rgb(255, 99, 132)', }, ], }, }); } } ``` ### Step 10: Performance Optimization Report ```apex public class PerformanceReportGenerator { public static void generateOptimizationReport() { Performance_Report__c report = new Performance_Report__c(); report.Report_Date__c = Date.today(); // Analyze query performance List<QueryAnalysis> slowQueries = analyzeSlowQueries(); report.Slow_Query_Count__c = slowQueries.size(); report.Query_Recommendations__c = generateQueryRecommendations(slowQueries); // Analyze code performance List<ApexAnalysis> inefficientCode = analyzeApexPerformance(); report.Code_Issues__c = inefficientCode.size(); report.Code_Recommendations__c = generateCodeRecommendations(inefficientCode); // Analyze page performance List<PageAnalysis> slowPages = analyzePagePerformance(); report.Slow_Page_Count__c = slowPages.size(); report.UI_Recommendations__c = generateUIRecommendations(slowPages); // Calculate overall health score report.Performance_Score__c = calculatePerformanceScore( slowQueries, inefficientCode, slowPages ); insert report; // Send report to stakeholders sendPerformanceReport(report); } } ``` ## Performance Best Practices ### Query Optimization 1. Use selective queries with indexed fields 2. Avoid SOQL in loops 3. Query only required fields 4. Use relationship queries efficiently 5. Implement query result caching ### Code Optimization 1. Bulkify all operations 2. Use collections efficiently 3. Minimize DML operations 4. Avoid recursive triggers 5. Use asynchronous processing ### UI Optimization 1. Lazy load components 2. Implement pagination 3. Use client-side caching 4. Minimize view state 5. Optimize asset delivery ## Success Criteria - [ ] Page load times < 3 seconds - [ ] No governor limit exceptions - [ ] Query execution < 100ms average - [ ] CPU time < 1000ms for transactions - [ ] Heap size < 3MB for most operations - [ ] User satisfaction scores improved ## References - [Salesforce Performance Best Practices](https://developer.salesforce.com/docs/atlas.en-us.salesforce_performance_best_practices.meta/salesforce_performance_best_practices/performance_overview.htm) - [Governor Limits](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm) - Optimization report template: `templates#optimization-report-tmpl.md`