Forum Discussion

mpegram3rd's avatar
mpegram3rd
New Contributor
15 years ago

Thread Safety issues in Mock Service as WAR

I am working with a client and was hoping to propose the use of SoapUI v3.6.1 mock services as a viable alternative to hand coded mock services. They use these mock services to provide stubs for back end functionality and to eliminate the back end when doing performance testing and analysis of the middle tier.

Given the problems we're seeing I would have a hard time, at this time, recommending the use of SoapUI to my client.

Before making this recommendation to the client, we wanted to perform some due diligence to make sure that SoapUI mock services could scale and not be a bottleneck. Our test methodology was to create a simple flat load model using LoadUI and direct it at the SoapUI generated Mock deployed as a WAR in a JavaEE container. We are not trying to use any of the "logging" features of the SoapUI mock, just simply wanted to see that it could respond in a timely manner under load.

What we found was that it started to throw NullPointerException under relatively low load. (A "scrubbed" stack trace is at the bottom of this email after my analysis).

Looking at the source code the fundamental issue has to do with the fact that the generated WAR file uses instance level variables to track "results" as well as a raft of other things. No locking or synchronization is done to make this thread safe, so after a fairly trivial amount of load, the servlet begins to fail, as it's trying to do multiple modifications to the list elements from different threads. The solution is to either make this thread safe through synchronization / locking (which of course impacts performance) or modify the manner in which you're tracking historical data.

There has been at least one other report on these forums of problems which is related to this specific issue. Here's link to that report:
http://www.eviware.com/forum/viewtopic.php?f=13&t=4651&p=15440&hilit=war#p15440

Here's the scrubbed stack trace from our issue:

####<Jan 5, 2011 3:23:23 PM EST> <Error> <HTTP> <XXXXXXX-XXXXXXX> <XXXXXXX> <[ACTIVE] ExecuteThread: '2' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1294259003274> <BEA-101020> <[weblogic.servlet.internal.WebAppServletContext@411af96 - appName: 'XXXXXMockServices', name: 'XXXXXMockServices', context-path: '/XXXXXMockServices', spec-version: '2.5'] Servlet failed with Exception
java.lang.NullPointerException
at org.apache.commons.collections.list.TreeList.remove(TreeList.java:222)
at com.eviware.soapui.mockaswar.MockAsWarServlet$MockServletSoapUICore.dispatchRequest(MockAsWarServlet.java:233)
at com.eviware.soapui.mockaswar.MockAsWarServlet.service(MockAsWarServlet.java:171)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3498)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2086)
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1406)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)
>


Your attention to this issue would be greatly appreciated!

Thank you,
Macon Pegram
  • Hi Macon,

    thanks for your efforts with this issue, we will definitely look into it. Have you run the same tests against the standalone mockservice runner? Or just soapUI running the mockservice?

    regards!

    /Ole
    eviware.com
  • mpegram3rd's avatar
    mpegram3rd
    New Contributor
    Ole thanks for replying.

    We tested both the commandline mockrunner and from within SOAP-UI and neither exhibited the problem. Our tests ran fine. Unfortunately that is not a viable solution for us because we need to co-deploy the mock service with the middleware code we are developing in a servlet container as a war.

    The thread safety issue actually is in the com.eviware.soapui.mockaswar.MockAsWarServlet code. In our case we most often see problems when the results.remove(0) call happens (however I suspect there may be some other issues given the prevalence of this type of instance member variable usage throughout the servlet).

    Here's the problem code block from the MockAsWarServlet:

    if( request.getPathInfo().equals( mockRunner.getMockService().getPath() ) )
    {
    MockResult result = mockRunner.dispatchRequest( request, response );
    while( maxResults > 0 && results.size() > maxResults )
    {
    results.remove( 0 );
    }
    if( result != null )
    {
    results.add( result );
    }
    return;
    }


    The issue here is that "results" is a class level variable, and since the servlet container creates only a single instance of the servlet, all threads that call this servlet are accessing the same "results" object. The Results object is an Apache Commons TreeList class which is not thread safe without the developer doing explicit synchronization.

    I suspect the alternative is for us to write a simpler version of the MockAsWarServlet without the extra features you provide in your implementation that causes the thread safety issues.

    Again thanks for looking in to this issue. In general I'm quite happy with Soap-UI, but this is definitely a show stopper for our intended use.
  • Hi!

    ok, thanks! I've added a synchronization block around this code, will be in the upcoming nightly build. Let me know how it works for you!

    regards,

    /Ole
    eviware.com
  • mpegram3rd's avatar
    mpegram3rd
    New Contributor
    Ole,

    Thanks... we'll check out the next nightly and give you some feedback on that tomorrow.

    Another suggestion though, it might be more ideal to allow the user to just turn off the "request" tracking feature completely via the web.xml. In this particular case we're using the mock service to fake a backend system so we can do load testing against our service consumer which sits in front of it. During load testing we'd never want to track requests. We'd want the mock to be as fast as possible so that we're measuring the performance of our code, not the limitations of the mock. Adding a synchronization block in there is going to hamper the mock's performance.

    Again, I appreciate your attention to this particular issue.
  • Hi!

    actually this is already possible, but not documented (until now) :-)

    You can add a "maxResults" initialization parameter to web.xml for the servlet and set it to 0, this will result in the mock service not saving any results at all, and I've added a check for this before going into the synchronized block. Coming in the next nightly!

    regards,

    /Ole
    eviware.com
  • mpegram3rd's avatar
    mpegram3rd
    New Contributor
    Looking good now Ole! Thanks. That definitely removes the obstacle from us recommending this approach to our client.

    Thanks for the fast response and addressing this issue.

    Macon