We use the REST module extensively in the OpenHMIS modules and we have noticed severe performance issues when converting largish result sets (50+ records plus ref sub-resources).
As a specific example, when returning a list of item stock for a stockroom in our Inventory module, the request for a paged list of 5 item stock records returns in around 500ms for around 12k of content. This is already pretty slow but when the size of the page is increased to 100 item stock records it takes around 4.5 seconds for about 150k of content.
My first thought was a content size or N+1 hibernate issue but neither of those were the culprit (though our resources could be stand to be a bit more smaller). Running a profiler (JProfiler) showed that the root cause was calls to two methods originating from the REST module:
- org.springframework.context.ApplicationContext.getBeansOfType (invoked 2,000+ times for a total of around 4 seconds)
- Originating from: org.openmrs.module.webservices.rest.web.ConversionUtil.getConverter
- java.lang.reflect.Method.getAnnotation (invoked 65,000+ times for a total of around 150ms)
- Originating from: org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription.evaluate
I have attached a JProfiler snapshot with full details and call traces; I can also try to export that information to a csv file if you don't have access to JProfiler.
The nice thing about both of these methods is that they are easily cacheable. I have attached a patch (from the 2.8 tag) which caches the ConversionUtil.getConverter, property getter method, and property setter method. Using this patched version, the item stock call for 100 records reduced to around 175ms (a 30x speed increase!). All the tests pass in this patched version and the caching is done is a thread-safe manner (using ConcurrentHashMap) but I did add a dependency to javatuples so that I could use the Pair class.
This patch merely addresses the hotspots that I noticed during my testing. It may make sense to look at this issue more holistically and ensure that all reflection and bean related actions are appropriately cached; perhaps within openmrs rather than in this module.