Most of the time we need metadata of entities and fields to make dynamic pages especially in the project that is being developed for the App Exchange marketplace. Schema class has two methods Schema.getGlobalDescribe() and Schema.describeSObjects() which are used to get metadata information. If we call these methods multiple times or in the loop, it will affect application performance. This post will give detail on how to optimize apex code by metadata caching.
Let us see both methods one by one.
1. Schema.getGlobalDescribe
Schema.getGlobalDescribe will return a huge list of all custom and standard objects, custom metadata definition, external objects, custom setting, and big objects. This list will vary based on org to org. If any org has so many objects then it will take a longer time to describe using this method. This method will create a complete map of all objects and if called multiple times, it will increase CPU time which will affect performance.
Take an example, the developer needs to get all custom settings of a client Org when the setting page is loaded. So he/she can use getGlobalDescribe method to get a list of call custom settings from client Org.
In the above code, we are getting all details using Schema.getGlobalDescribe.
Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
Schema.getGlobalDescribe is a heavy process and if we are calling this method multiple times, it is a big problem for application. So what are possible ways to handle this properly?
- Use static property
- Use Caching mechanism to store the output
Let us see both approaches one by one to make better code.
i. Use Static property
We can create a static property that will call Schema.getGlobalDescribe(); only once when the first time method is called. As it is a static property, it will be called only once in one transaction. This will be useful when we are calling describing method in loops.
static Map<String, Schema.SObjectType> globalDescribe=null;
public static Map<String, SObjectType> globalDescribe {
get { if(globalDescribe == null) globalDescribe = Schema.getGlobalDescribe();
return globalDescribe;
}
}
ii. Use Caching mechanism to store the output
The above solution will work in the case of one transaction, what if we need it in another transaction or needed by another user. For this, we have to store the output in cache location. We can use Session or Org cache. We should go with the Org cache as this will be helpful for other users as well. So now the above implementation will be updated like the below code. I have created one platform cache (schema) to test this implementation.
When this code is executed first time, it will get metadata from the global describe call. When getCustomSetting() is called the second time it will get the result from the cache. Right now we have just cached the result of Schema.getGlobalDescribe(). If we need to cache query string (after line#37), that also can be cached. This will be dependent on the use case and individual requirements.
If org has very few custom objects then this will not give improvement to performance. Might be it will decrease performance as well as putting/retrieving data from the Platform Cache will take time also.
Testing above approaches:
Test the above code using the below code line. You can check the time taken for global describe. This code can be executed multiple times to see CPU time is being saved or not.
When I have tested DescribeService.getCustomSetting() in one of the org where around 6 custom settings were there. Here is the time taken by code when multiple time methods are called. Again this can be dependent on the number of custom settings as well in your org.
2. Schema.describeSObjects
We should only call Schema.getGlobalDescribe() when you are not aware of the object but if you know the object name or you have very few objects to describe, We can use Schema.describeSObjects method for the metadata describes.
Schema.describeSObjects will return metadata (field list and object properties) for the specified sObject or array of sObjects. This can also create a performance issue if we call this multiple times or in the loop. We should cache result of object describe for later use.
When we don’t know the object name in advance and we need to describe a few objects then below is a better approach. We can use Type newInstance method to get metadata.
Metadata information should be stored in the cache for later use.
Summary:
- Methods Schema.getGlobalDescribe() or Schema.describeSObjects should be called once per an apex transaction
- Methods Schema.getGlobalDescribe() or Schema.describeSObjects should never be called inside a loop or repeatedly.
- If we need the describe information for every object in the organization, the method Schema.getGlobalDescribe() should be used and its results should be cached.
- If we need the describe information for known many objects, the method Schema.describeSObjects() should be used with a parameter of the list of object names. The result should be cached and reused instead of the calling method again.
- If we need to describe information for a few unknown objects then we can use the Type instance approach.
Old posts related to Performance Optimization
Optimizing Salesforce Apex Code
Optimize Code by Disabling Debug Mode
6 comments
excellent post.
Thank You Kishan.
[…] Optimize Apex Code by Metadata Caching […]
[…] Optimize Apex Code by Metadata Caching […]
[…] Optimize Apex Code by Metadata Caching […]
[…] Stop Describing every time and use Caching of the described object […]