[SWS-1000] java.util.Hashtable limits the throughput of org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive Created: 23/Oct/17 Updated: 07/Nov/17 Resolved: 07/Nov/17
|Project:||Spring Web Services|
|Resolution:||Works as Designed||Votes:||0|
|Remaining Estimate:||Not Specified|
|Time Spent:||Not Specified|
|Original Estimate:||Not Specified|
|Attachments:||Contention.png Contention.png Contention_removed.png E2ELatency.png|
org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive uses java.util.Hashtable.get(Object) implementation to the get url stream handler for marshaling.
This HashTable implementation is known to be quite poor in terms of performance in a highly concurrent environment. Latency/throughput of the implementing application can be found below.
I'm attaching the JFR stack trace of the related calls, showing contention. This increases as the concurrency increases.
Can an alternative implementation, like the one mentioned at https://issues.apache.org/jira/browse/SPARK-16826, be used to circumvent this issue?
|Comment by Greg Turnquist [ 24/Oct/17 ]|
For starters, the gap between WebServiceTemplate, where our library code is, and the actual usage of a Hashtable is over 30 layers deep. So to say that Spring WS is using a Hashtable is a faulty statement. Spring Web Services is leveraging SAAJ or the SOAP with Attachments API for Java, hence the code using a Hashtable would be standard Java.
In essence, Spring Web Services is taking a string-based URI and asking Java to take if from there in constructing a SAAJ message. Any alteration in the efficiency of SAAJ would entail a bug report with the SAAJ spec lead.
WebServiceTemplate has a DestinationProvider interface, which includes a cached solution that supports caching URIs after they are parsed from objects, but your stack trace doesn't appear to invoke the getDefaultUri() call that would invoke that.
If you provided a little more information about your use case and how you think this is Spring Web Services making an inefficient call, then perhaps we can unravel the real issue you are running into.
|Comment by Sasidhar Sekar [ 25/Oct/17 ]|
The client code looks like this:
When a request comes in for RequestTypeOne, the client invokes retrieveRequestTypeOne and likewise for RequestTypeTwo, which in turn pass the request object and the endpoint url to getWebServiceTemplate().marshalSendAndReceive(microserviceUrl, request).
|Comment by Greg Turnquist [ 27/Oct/17 ]|
I guess what you have described sounds like a conventional approach. I don't know what your load factor is, but we are talking about blocking APIs when we discuss SOAP. (People have put in requests for Reactor-based APIs that I have had to decline).
Since we are talking microservices, do you have a cloud-based solution in place? I didn't know if you were using Spring Cloud components like Spring Cloud Netflix Eureka or Spring Cloud Ribbon. Because if you have a pool of microservices, it would make sense to round robin between them for various calls. Whereas Spring Web Services doesn't support Eureka-based URL resolution, it probably wouldn't be too difficult to programmatically get that URL and hence simplify remote calls to a pool of SOAP microservices through service discovery.
|Comment by Sasidhar Sekar [ 30/Oct/17 ]|
We are not on cloud yet. The servers are physical, within our datacenter. But, we do have multiple instances behind a VIP to cover for this at the moment. The only concern is that we are unable to use the physical servers to their full capacity at the moment.
|Comment by Sasidhar Sekar [ 07/Nov/17 ]|
Implementing the AbstractCachingDestinationProvider has infact resolved the contention observed on URL resolution. A comparison of the url resolution attempts before and after could be found below.
As can be seen, only one call is made to resolve the url (marked by the HashTable.get())
Thank you so much for your help with this, Greg. I think this is resolved now.
|Comment by Greg Turnquist [ 07/Nov/17 ]|
Glad to help you resolve your issue.