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
Markdown
**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
- System Administrator access
- Performance baseline metrics
- Access to debug logs
- Understanding of governor limits
- Query plan access
- Performance monitoring tools
```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;
}
}
```
```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;
}
}
```
```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>(
[]
);
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...
}
}
}
```
```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;
}
}
}
```
```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>
```
```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;
}
}
}
```
```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);
}
}
}
```
```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'));
}
}
}
```
```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)',
},
],
},
});
}
}
```
```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);
}
}
```
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
1. Bulkify all operations
2. Use collections efficiently
3. Minimize DML operations
4. Avoid recursive triggers
5. Use asynchronous processing
1. Lazy load components
2. Implement pagination
3. Use client-side caching
4. Minimize view state
5. Optimize asset delivery
- [ ] 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
- [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