6 Replies Latest reply on Jul 10, 2013 1:14 PM by mashama

    Load Testing ModeShape Versioning

    mashama

      I need some help understanding the behavior of the versioning system in the context of a rather unrealistic load test.  The version history json output from modeshape-rest is attached.  Basicly I have 10 concurrent users utilizing 2 connections to write a 75KB file to a nt:file node that is versioned.  What concerns me are snippets like:

       

          "1.26[9]": {

            "self": "http://chp-ws70:8080/modeshape-rest/salesapp/carrier-group-2170/items/jcr:system/jcr:versionStorage/c0/df/9c/c0df9c793f462fb08f64cd08a49534819d288acb/1.26[9]",

            "up": "http://chp-ws70:8080/modeshape-rest/salesapp/carrier-group-2170/items/jcr:system/jcr:versionStorage/c0/df/9c/c0df9c793f462fb08f64cd08a49534819d288acb",

            "id": "61a53fc317f1e70b6e7d6c-fc38-4408-a904-b64e71bbfa11"

          },

          "1.26[10]": {

            "self": "http://chp-ws70:8080/modeshape-rest/salesapp/carrier-group-2170/items/jcr:system/jcr:versionStorage/c0/df/9c/c0df9c793f462fb08f64cd08a49534819d288acb/1.26[10]",

            "up": "http://chp-ws70:8080/modeshape-rest/salesapp/carrier-group-2170/items/jcr:system/jcr:versionStorage/c0/df/9c/c0df9c793f462fb08f64cd08a49534819d288acb",

            "id": "61a53fc317f1e7818be43e-570d-4297-ae94-11d23725cb86"

          },

       

      In the above json snippet you see version numbers like 1.26[9] and 1.26[7].  I have seen the bracketed numbers before in the context of overwriting the same nt:file or nt:folder node where that node is not versioned.  It seems like in those cases the bracketed numbers appended to the node relative path name to get around the issue of not being able to add sibiling nodes with the same relative path.  I am worried that by virtue of this happening version history is actually being lost.  Locking at the following snippet you will see that the predecessor does not actually point to the previous bracketed number version.

       

      "jcr:predecessors": ["http://chp-ws70:8080/modeshape-rest/salesapp/carrier-group-2170/items/jcr:system/jcr:versionStorage/c0/df/9c/c0df9c793f462fb08f64cd08a49534819d288acb/jcr:rootVersion"],

        "jcr:uuid": "47daec1d-a062-4f5f-a2d9-f7623a143284",

        "children": {"jcr:frozenNode": {

          "self": "http://chp-ws70:8080/modeshape-rest/salesapp/carrier-group-2170/items/jcr:system/jcr:versionStorage/c0/df/9c/c0df9c793f462fb08f64cd08a49534819d288acb/1.0[3]/jcr:frozenNode",

          "up": "http://chp-ws70:8080/modeshape-rest/salesapp/carrier-group-2170/items/jcr:system/jcr:versionStorage/c0/df/9c/c0df9c793f462fb08f64cd08a49534819d288acb/1.0[3]",

          "id": "61a53fc317f1e76687eeb5-237e-4fde-b488-8313b568d8c3"

        }}

       

      I realize that I probably should be doing something at the application level to deal with this, but what that is (locking, checking isCheckedOut, merging, etc.) I have yet to ascertain. I just wanted to reach out to your team and see if you had any thoughts about this.  Thanks again! 

       

      #ModeShapeROCKS!

        • 1. Re: Load Testing ModeShape Versioning
          rhauch

          The output you've given is the JSON response from our RESTful service. Does that mean that you're load testing by going through the RESTful service? Or, are you just displaying the version history of a node via the RESTful service and using the JCR API for your load testing path?

           

          Is this a non-clustered or clustered topology?

           

          What the output suggests is that there are multiple concurrent threads that are all calling 'checkin' at nearly the same time. The "checkin" logic first verifies that the node has not been modified transiently in the session, and then checks that the "jcr:isCheckedOut" property is set to true. If both are true (plus a few other checks), the method proceeds to check in the node's persisted state.

           

          The strange (even bizarre) thing about JCR versioning is that the checked-out state is not "exclusive" but is in fact global -- it doesn't really record which session checked it out and it doesn't prevent other sessions from modifying the state while one session has it checked out.

           

          Therefore, probably the best way forward is to obtain a session-scoped lock for each node that is being checked in. Although it's difficult for me to say without knowing what those separate threads are doing, I'm going to guess that it's appropriate to get the lock before checkout, since that would ensure that you can checkout the node, make the changes, save, and check it in all within a single, atomic, isolated fashion.

           

          Alternatively, if you have JTA in your environment you might try creating user transactions around the "checkout, make changes, save, and checkin" operation. I've not actually tried it, but theoretically it might work and would be a bit more straightforward.

           

          Hope this helps!

          • 2. Re: Load Testing ModeShape Versioning
            mashama

            Hi Randall,

             

            I am load testing an application that uses the JCR API in a non-clustered topology and simply displaying the version history through the RESTful service.  Thanks for the additionaI information regarding the checkin logic.  I will investigate both the suggestions you provided!

             

            m.

            • 3. Re: Load Testing ModeShape Versioning
              mashama

              So I started to investigate the locking solution first and immediately ran into some difficulties.  I am not sure why I am getting a RepositoryException when attempting to lock the given path.

               

               

              LockManager lockManager = session.getWorkspace().getLockManager();
              lockManager.lock(fileNode.getPath(), true, true, Long.MAX_VALUE, this.toString());
              
              VersionManager versionManager = session.getWorkspace().getVersionManager();
              versionManager.checkout(fileNode.getPath());
              
              Binary binary = session.getValueFactory().createBinary(new ByteArrayInputStream(outputStream.toByteArray()));
              contentNode.setProperty("jcr:data", binary);
              contentNode.setProperty("jcr:mimeType", dataSource.getAlfrescoName().toLowerCase().contains(".xlsx")?"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":"application/vnd.ms-excel");
              contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
              
              session.save();
              
              versionManager.checkin(fileNode.getPath());
              

               

               

              12:34:04,294 ERROR [com.chp.valuequest.controller.RESTfulService] (http-CHP-WS70/192.168.62.46:8080-1) javax.jcr.RepositoryException: java.lang.NullPointerException: javax.jcr.RepositoryException: java.lang.NullPointerException
                        at org.modeshape.jcr.RepositoryLockManager.lock(RepositoryLockManager.java:227) [modeshape-jcr-3.3.0.Final.jar:3.3.0.Final]
                        at org.modeshape.jcr.JcrLockManager.lock(JcrLockManager.java:256) [modeshape-jcr-3.3.0.Final.jar:3.3.0.Final]
                        at org.modeshape.jcr.JcrLockManager.lock(JcrLockManager.java:222) [modeshape-jcr-3.3.0.Final.jar:3.3.0.Final]
                        at com.chp.valuequest.controller.RESTfulService.storeCensusDataFile(RESTfulService.java:228) [classes:]
                        at com.chp.valuequest.controller.RESTfulService$Proxy$_$$_WeldClientProxy.storeCensusDataFile(RESTfulService$Proxy$_$$_WeldClientProxy.java) [classes:]
                        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_29]
                        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) [rt.jar:1.6.0_29]
                        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) [rt.jar:1.6.0_29]
                        at java.lang.reflect.Method.invoke(Unknown Source) [rt.jar:1.6.0_29]
                        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:269) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:227) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:216) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:542) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:126) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:208) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:50) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]
                        at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-1.jar:1.0.2.Final-redhat-1]
                        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:295) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:149) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.2.0.Final-redhat-8.jar:7.2.0.Final-redhat-8]
                        at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.2.0.Final-redhat-8.jar:7.2.0.Final-redhat-8]
                        at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169) [jboss-as-web-7.2.0.Final-redhat-8.jar:7.2.0.Final-redhat-8]
                        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:145) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:336) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:920) [jbossweb-7.2.0.Final-redhat-1.jar:7.2.0.Final-redhat-1]
                        at java.lang.Thread.run(Unknown Source) [rt.jar:1.6.0_29]
              Caused by: java.lang.NullPointerException
                        at org.modeshape.jcr.cache.document.SessionNode.updateReferences(SessionNode.java:768) [modeshape-jcr-3.3.0.Final.jar:3.3.0.Final]
                        at org.modeshape.jcr.cache.document.SessionNode.setProperty(SessionNode.java:743) [modeshape-jcr-3.3.0.Final.jar:3.3.0.Final]
                        at org.modeshape.jcr.RepositoryLockManager.lock(RepositoryLockManager.java:209) [modeshape-jcr-3.3.0.Final.jar:3.3.0.Final]
                        ... 35 more
              
              

               

              Am I doing something wrong here?

              • 4. Re: Load Testing ModeShape Versioning
                rhauch

                I presume that the exception was caused by your second line -- is that true? What happened before you're calling "lock"? Is there any modified transient state in a session? For example, what does "fileNode" refer to?

                1 of 1 people found this helpful
                • 5. Re: Load Testing ModeShape Versioning
                  mashama

                  Yes and ... yes!  Calling Session.save() to save the transient save first resolved the issue.

                  • 6. Re: Load Testing ModeShape Versioning
                    mashama

                    Obtaining "a session-scoped lock for each node that is being checked in" resolved the issue with the version numbers.