9 Replies Latest reply on Aug 12, 2008 10:42 AM by damianharvey.damianharvey.gmail.com

    Performance like a line of schoolkids awaiting first Jab.

    tony.herstell1

      I have a page that is running appallingly slow.


      I have provided code below.


      Question 1: Can anyone spot the problem?


      Question 2: What tools are recommended for analysing the problem.


      This is running on Prod just as slow (if not slower) than Dev.



      There are a lot of set/gets as @Out with boolean doesn't work so you have to add routines (not that I even found why in the manual). Another little quirk of Seam I guess. Have code with @Out for most private values and then put/get for booleans...



      Given this:


      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
           xmlns:s="http://jboss.com/products/seam/taglib" xmlns:a4j="http://richfaces.org/a4j"
           xmlns:rich="http://richfaces.org/rich"
           template="/WEB-INF/pages/templateCleanWithConversations.xhtml">
      
           <!-- content -->
           <ui:define name="template_title">
                <h:outputText value="#{messages.user_list_page}" />
           </ui:define>
      
           <ui:define name="template_content">
      
                <div id="reg_form">
      
                     <h:form class="general_form">
      
                          <fieldset class="general_form_fieldset">
      
                               <legend class="general_form_legend">
                                    <h:outputText value="#{messages.user_list_fieldset}" />
                               </legend>
      
                               <s:validateAll>
                               
                                    <!-- CONTROLS -->
                                    <div class="left">
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_avatar" value="Show Avatar"/>
                                              <h:selectBooleanCheckbox id="show_avatar" value="#{userListController.showingAvatar}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_email" value="Show Email"/>
                                              <h:selectBooleanCheckbox id="show_email" value="#{userListController.showingEmail}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_phone" value="Show Phone"/>
                                              <h:selectBooleanCheckbox id="show_phone" value="#{userListController.showingPhone}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_dressage" value="Show Dressage"/>
                                              <h:selectBooleanCheckbox id="show_dressage" value="#{userListController.showingDressage}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_sj" value="Show SJ"/>
                                              <h:selectBooleanCheckbox id="show_sj" value="#{userListController.showingSJ}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_showing" value="Show Showing"/>
                                              <h:selectBooleanCheckbox id="show_showing" value="#{userListController.showingShowing}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_events" value="Show Events"/>
                                              <h:selectBooleanCheckbox id="show_events" value="#{userListController.showingEvents}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                         <span class="padded_right_left">
                                              <h:outputLabel for="show_dates" value="Show Dates"/>
                                              <h:selectBooleanCheckbox id="show_dates" value="#{userListController.showingDates}">
                                                   <a4j:support event="onclick" eventsQueue="eventsQueue" LimitToList="true" reRender="users_list" />
                                              </h:selectBooleanCheckbox>
                                         </span>
                                    </div>
      
                                    <div>
                                         <br/>
                                         <br/>
                                    </div>
      
                                    <!-- TABLE -->
                                    <rich:dataTable id="users_list" value="#{userList}" var="eachUser"
                                         columnClasses="center" rows="20" width="100%">
                                         <f:facet name="header">
                                              <h:outputText value="Users" />
                                         </f:facet>
                                         
                                         <rich:column rendered="#{userListController.isShowingAvatar() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Avatar" />
                                              </f:facet>
                                              <s:graphicImage width="75px" rendered="#{eachUser.avatar ne null}" value="#{eachUser.avatar.image}" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.surname}">
                                              <f:facet name="header">
                                                   <h:outputText value="Surname" />
                                              </f:facet>
                                              <h:outputText value="#{eachUser.surname}" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.firstname}">
                                              <f:facet name="header">
                                                   <h:outputText value="Firstname" />
                                              </f:facet>
                                              <h:outputText value="#{eachUser.firstname}" />
                                         </rich:column>
                                         <rich:column rendered="#{userListController.isShowingEmail() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Email" />
                                              </f:facet>
                                              <h:outputText value="#{eachUser.email}" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.homePhone}" rendered="#{userListController.isShowingPhone() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Home Phone" />
                                              </f:facet>
                                              <h:outputText value="#{eachUser.homePhone}" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.mobilePhone}" rendered="#{userListController.isShowingPhone() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Mobile Phone" />
                                              </f:facet>
                                              <h:outputText value="#{eachUser.mobilePhone}" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.phoneContactOk}" rendered="#{userListController.isShowingPhone() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Phone Contact Ok" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.phoneContactOk}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.phoneContactOk}" url="/images/cross.gif" />
                                         </rich:column>
                                         
                                         <rich:column sortBy="#{eachUser.dressageJudge}" rendered="#{userListController.isShowingDressage() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Dressage Judge" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.dressageJudge}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.dressageJudge}" url="/images/cross.gif" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.dressageWriter}" rendered="#{userListController.isShowingDressage() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Dressage Writer" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.dressageWriter}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.dressageWriter}" url="/images/cross.gif" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.dressageHelper}" rendered="#{userListController.isShowingDressage() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Dressage Helper" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.dressageHelper}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.dressageHelper}" url="/images/cross.gif" />
                                         </rich:column>
      
      
                                         <rich:column sortBy="#{eachUser.sjJudge}" rendered="#{userListController.isShowingSJ() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="SJ Judge" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.sjJudge}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.sjJudge}" url="/images/cross.gif" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.sjHelper}" rendered="#{userListController.isShowingSJ() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="SJ Helper" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.sjHelper}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.sjHelper}" url="/images/cross.gif" />
                                         </rich:column>
                                         
                                         <rich:column sortBy="#{eachUser.showingJudge}" rendered="#{userListController.isShowingShowing() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Showing Judge" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.showingJudge}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.showingJudge}" url="/images/cross.gif" />
                                         </rich:column>
                                         <rich:column sortBy="#{eachUser.showingHelper}" rendered="#{userListController.isShowingShowing() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Showing Helper" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.showingHelper}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.showingHelper}" url="/images/cross.gif" />
                                         </rich:column>
                                         
                                         <rich:column sortBy="#{eachUser.eventsHelper}" rendered="#{userListController.isShowingEvents() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Events Helper" />
                                              </f:facet>
                                              <s:graphicImage rendered="#{eachUser.eventsHelper}" url="/images/tick.gif" />
                                              <s:graphicImage rendered="#{not eachUser.eventsHelper}" url="/images/cross.gif" />
                                         </rich:column>
                                         
                                         <rich:column sortBy="#{eachUser.dates.creationDate}" rendered="#{userListController.isShowingDates() eq true}">
                                              <f:facet name="header">
                                                   <h:outputText value="Creation Date" />
                                              </f:facet>
                                              <h:outputText value="#{eachUser.dates.creationDate}">
                                                   <s:convertDateTime pattern="dd/MMM/yyyy"/>
                                              </h:outputText>
                                         </rich:column>
                                         
                                    </rich:dataTable>
      
                                    <rich:datascroller for="users_list" maxPages="30"/>
      
                               </s:validateAll>
      
                               <div class="general_form_buttons">
                                    <h:commandButton class="general_form_button" action="#{userListController.cancel}"
                                         value="#{messages.general_button_cancel}" immediate="true" type="submit">
                                         <s:conversationPropagation type="end" />
                                    </h:commandButton>
                               </div>
      
                          </fieldset>
      
                     </h:form>
      
                </div>
                
                <a4j:status>
                     <f:facet name="start">
                          <h:graphicImage styleClass="page_foot_image" value="/images/ajax/animated_horse.gif" height="50" />
                     </f:facet>
                </a4j:status>
      
           </ui:define>
           <!-- content -->
      </ui:composition>
      



      Running over this code:


      package nz.co.selwynequestriancentre.action.user;
      
      import java.io.Serializable;
      
      import javax.ejb.Remove;
      import javax.ejb.Stateful;
      import javax.ejb.TransactionAttribute;
      import javax.ejb.TransactionAttributeType;
      import javax.persistence.EntityManager;
      import javax.persistence.PersistenceContext;
      import javax.persistence.PersistenceContextType;
      
      import nz.co.selwynequestriancentre.model.entity.User;
      
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.Begin;
      import org.jboss.seam.annotations.Conversational;
      import org.jboss.seam.annotations.Destroy;
      import org.jboss.seam.annotations.End;
      import org.jboss.seam.annotations.Factory;
      import org.jboss.seam.annotations.Logger;
      import org.jboss.seam.annotations.Name;
      import org.jboss.seam.annotations.Scope;
      import org.jboss.seam.annotations.datamodel.DataModel;
      import org.jboss.seam.log.Log;
      
      import java.util.List;
      
      /**
       * @author Tony Herstell
       * @version $Revision: 1.2 $ $Date: 2008-08-11 07:09:14 $
       */
      @SuppressWarnings("serial")
      @Stateful
      @Name("userListController")
      @Conversational
      @Scope(value=ScopeType.CONVERSATION)
      public class UserListControllerImpl implements UserListController, Serializable {
      
          /**
           * Inject and leverage the Seam Logger.
           */
          @Logger
          private Log log;
           
           /**
            * Inject the EJB3 Persistence context in EXTENDED mode so will remain
            * "current" over multiple client/server round trips.
            */
           @PersistenceContext(type=PersistenceContextType.EXTENDED)
           private EntityManager em;
      
           @DataModel
           private List<User> userList;
      
           private boolean showingAvatar = false;
           private boolean showingEmail = false;
           private boolean showingPhone = true;
           private boolean showingDressage = true;
           private boolean showingSJ = false;
           private boolean showingShowing = false;
           private boolean showingEvents = false;
           private boolean showingDates = false;
      
           @Factory("userList")
           @TransactionAttribute(TransactionAttributeType.REQUIRED)
           public void findUsers()
           {
                userList = em.createQuery("from User user order by user.surname asc").getResultList();
           }
           
           @TransactionAttribute(TransactionAttributeType.REQUIRED)
           @Begin
           public String enter() {
                return "userListController";
           }
           
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingAvatar() {
               return showingAvatar;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingAvatar(boolean showingAvatar) {
               this.showingAvatar = showingAvatar;
          }
           
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingEmail() {
               return showingEmail;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingEmail(boolean showingEmail) {
               this.showingEmail = showingEmail;
          }
           
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingPhone() {
               return showingPhone;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingPhone(boolean showingPhone) {
               this.showingPhone = showingPhone;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingDressage() {
               return showingDressage;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingDressage(boolean showingDressage) {
               this.showingDressage = showingDressage;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingSJ() {
               return showingSJ;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingSJ(boolean showingSJ) {
               this.showingSJ = showingSJ;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingShowing() {
               return showingShowing;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingShowing(boolean showingShowing) {
               this.showingShowing = showingShowing;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingEvents() {
               return showingEvents;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingEvents(boolean showingEvents) {
               this.showingEvents = showingEvents;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public boolean isShowingDates() {
               return showingDates;
          }
      
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
           public void setShowingDates(boolean showingDates) {
               this.showingDates = showingDates;
          }
           
           @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
          @End
          public String cancel() {
              log.info(">cancel");
              log.info("<cancel");
              return "home";
          }
      
          @Remove
          @Destroy
          @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
          public void destroy() {
              log.info("> destory");
              log.info("< destory");
          }
          
      }



        • 1. Re: Performance like a line of schoolkids awaiting first Jab.
          dhinojosa

          The


             @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
          



          is throwing flags in my mind since Seam automatically provides a global transaction with your lifecycle, and you are telling it that you aren't supporting transactions at all.
          What happens if you disable those for now, and let the Seam's own transaction manager handle that for you?


          • 2. Re: Performance like a line of schoolkids awaiting first Jab.
            tony.herstell1

            Ok I will try this tonight.


            This construct was in Micheal Yauns book and solved other problems of transations running over too may part of the transaction. I will re-read the pages...


            I have other pages/code combinations that do a huge amount more stuff than this to get a page on the screen; ill add EXTENDED into that and see what happnes as well.




            :)


            • 3. Re: Performance like a line of schoolkids awaiting first Jab.
              dhinojosa

              Interesting, I will check that book out.  I could be wrong, I'll take a look at more of your code.

              • 4. Re: Performance like a line of schoolkids awaiting first Jab.
                sjmenden

                I can add something from personal experience, rich:dataTable is twice as slow as h:dataTable, so test the performance switching back to h:dataTable.  I experienced this with Richfaces 3.2.1.GA, not sure if it holds true for other versions.


                Also, check out http://facestrace.sourceforge.net/  which will show the amount of time spent in each phase.  It will be interesting to see how much time is spend in INVOKE versus RENDER, because if all the time is in INVOKE, then you have slowdowns in your Bean, if all time is in RENDER, then it is with the EL/Components.

                • 5. Re: Performance like a line of schoolkids awaiting first Jab.
                  tony.herstell1

                  9.3. Atomic Conversation (Web Transaction)
                  The Seam/EJB3 transaction is tied to a Java thread. It can manage operations only within a
                  method call stack. It flushes all updates to the database at the end of each call stack. That behavior
                  has two problems in a web conversation:
                  First, it can be inefficient to make multiple round-trips to the database in a conversation when
                  all the updates are closely related.
                  Second, when a certain operation in the conversation fails, you must manually roll back the
                  already-committed transactions in the conversation to restore the database to its state prior to
                  the conversation.
                  A much better way is for the application to hold all database updates in memory and flush
                  them all at once at the end of the conversation. If an error occurs in any step in the conversation,
                  the conversation just fails without affecting the database. From the database point of
                  view, the entire conversation either succeeds or fails—hence, atomic conversation. The atomic
                  conversation behavior is also known as a web transaction. Seam makes it easy to implement
                  atomic conversations. In the following sections, we discuss two approaches. The first is
                  for Seam POJOs, and the second is for EJB3 session beans.


                  SNIP SNIP


                  9.3.2. One Transaction per Conversation
                  Another alternative is to disable the transaction manager on all methods except for the @End
                  method. Because this approach requires method-level transaction demarcation, it can be used
                  only on EJB3 session bean components with an EJB3-managed EntityManager (i.e., an
                  EntityManager injected via @PersistenceContext).
                  9.3.2. One Transaction per Conversation
                  133
                  This method is not as outrageous as it might sound. The transaction manager is not flushing
                  anything to the database before the end of the conversation, so there is nothing to roll back
                  if an error occurs. At the @End method, the data is automatically flushed to the database in a
                  properly managed transaction. This is done by declaring all nontransactional methods in a
                  conversation with the @TransactionAttribute annotation. Consider this example:


                  public class HotelBookingAction
                  implements HotelBooking, Serializable {
                  // ... ...
                  @PersistenceContext (type=EXTENDED)
                  private EntityManager em;
                  @Begin(join=true)
                  @TransactionAttribute(
                  TransactionAttributeType.NOT_SUPPORTED)
                  public String find() {
                  // ... ...
                  }
                  @TransactionAttribute(
                  TransactionAttributeType.NOT_SUPPORTED)
                  public String bookHotel()
                  throws InventoryException {
                  // ... ...
                  hotel.reduceInventory ();
                  }
                  @End
                  @TransactionAttribute(
                  TransactionAttributeType.REQUIRED)
                  public String confirm() {
                  // ... ...
                  em.persist (booking);
                  }
                  }



                  Because this approach uses only EJB3 standard annotations, it works in all EJB3-compliant
                  application servers.

                  • 6. Re: Performance like a line of schoolkids awaiting first Jab.
                    tony.herstell1

                    Twice as fast is interesting, but this is SOOOO long its virtually unusable.. and its only drawing a list of 20 users on the screen.


                    I will try this tonight and see wht it comes up with.


                    Cheers Samuel.

                    • 7. Re: Performance like a line of schoolkids awaiting first Jab.
                      sjmenden

                      my list had about 10 items in it, rich:dataTable took 7 seconds, h:dataTable 3-4, this was an EL heavy table is why those numbers are so large to begin with.

                      • 8. Re: Performance like a line of schoolkids awaiting first Jab.
                        tony.herstell1

                        Humm.. I can access the site from work.


                        9 seconds for intial list presentation.


                        18 seconds if I click on a table heading to sort by that heading.


                        I will try this tonight and see what it comes up with.

                        • 9. Re: Performance like a line of schoolkids awaiting first Jab.
                          damianharvey.damianharvey.gmail.com

                          I use rich:dataTable heavily and haven't noticed any problems with it. Mileage varies I guess. I don't use the filtering or sorting. Maybe they have more of an impact?


                          I haven't tried the FacesTrace but one thing that I've found invaluable is the Bean-Timing Interceptor that Tobias Hill posted here a while ago. I use it to ascertain whether I'm spending too much time in any particular method especially those used for rendering.


                          Cheers,


                          Damian.