Kuali Rice Development
  1. Kuali Rice Development
  2. KULRICE-8946

A few small things that can improve KRAD performance

    Details

    • Type: Improvement Improvement
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.2
    • Fix Version/s: 2.3.1
    • Security Level: Public (Public: Anyone can view)
    • Labels:
      None
    • Similar issues:
      KULRICE-9597Script logic performance pass
      KULRICE-10260Analyze performance in targeted KRAD views
      KULRICE-3515Performance improvements to creation of ValidActionsDTO
      KULRICE-12476KRAD performance issues with OLE
      KULRICE-14239Performance improvements of AFT tests
      KULRICE-1130improve performance of xml ingestion
      KULRICE-8918Improved KRAD collections performance
      KULRICE-8324Performance improvements for totalling and group totalling
      KULRICE-14090Make improvements to KIM integration performance
      KULRICE-4421SessionDocumentService can be a performance bottleneck
    • Epic Link:
    • Rice Module:
      KRAD
    • KRAD Feature Area:
      UIF MVC
    • Application Requirement:
      KS
    • Sprint:
      2.3.1 Sprint 2
    • KAI Review Status:
      Not Required
    • KTI Review Status:
      Not Required

      Description

      1 - make the parser static. It's supposedly threadsafe.

          protected static ExpressionParser parser = new SpelExpressionParser();
      

      2 - add custom functions is expensive and is called over and over.
      use static variables to store all the methods registered:

          private static Method isAssignableFrom;
          private static Method empty;
          private static Method emptyList;
          private static Method listContains;
          private static Method getName;
          private static Method getParm;
          private static Method getParmInd;
          private static Method hasPerm;
          private static Method hasPermDtls;
          private static Method hasPermTmpl;
          private static Method sequence;
      
          static{
              try{
                  isAssignableFrom = ExpressionFunctions.class.getDeclaredMethod("isAssignableFrom", new Class[]{Class.class, Class.class});
                  empty = ExpressionFunctions.class.getDeclaredMethod("empty", new Class[]{Object.class});
                  emptyList = ExpressionFunctions.class.getDeclaredMethod("emptyList", new Class[]{List.class});
                  listContains = ExpressionFunctions.class.getDeclaredMethod("listContains", new Class[]{List.class, Object[].class});
                  getName = ExpressionFunctions.class.getDeclaredMethod("getName", new Class[]{Class.class});
                  getParm = ExpressionFunctions.class.getDeclaredMethod("getParm", new Class[]{String.class, String.class, String.class});
                  getParmInd = ExpressionFunctions.class.getDeclaredMethod("getParmInd", new Class[]{String.class, String.class, String.class});
                  hasPerm = ExpressionFunctions.class.getDeclaredMethod("hasPerm", new Class[]{String.class, String.class});
                  hasPermDtls = ExpressionFunctions.class.getDeclaredMethod("hasPermDtls", new Class[]{String.class, String.class, Map.class, Map.class});
                  hasPermTmpl = ExpressionFunctions.class.getDeclaredMethod("hasPermTmpl", new Class[]{String.class, String.class, Map.class, Map.class});
                  sequence = ExpressionFunctions.class.getDeclaredMethod("sequence", new Class[]{String.class});
              }catch(NoSuchMethodException e){
                  LOG.error("Custom function for el expressions not found: " + e.getMessage());
                  throw new RuntimeException("Custom function for el expressions not found: " + e.getMessage(), e);
              }
          }
          protected void addCustomFunctions(StandardEvaluationContext context) {
                  // TODO: possibly reflect ExpressionFunctions and add automatically
              context.registerFunction("isAssignableFrom", isAssignableFrom);
              context.registerFunction("empty", empty);
              context.registerFunction("emptyList", emptyList);
              context.registerFunction("listContains", listContains);
              context.registerFunction("getName", getName);
              context.registerFunction("getParm", getParm);
              context.registerFunction("getParmInd", getParmInd);
              context.registerFunction("hasPerm", hasPerm);
              context.registerFunction("hasPermDtls", hasPermDtls);
              context.registerFunction("hasPermTmpl", hasPermTmpl);
              context.registerFunction("sequence", sequence);
      
          }
      

      3 - creating evaluation context multiple times is expensive, especially if it does not change.
      ViewHelperServiceImpl.performComponentApplyModel calls evaluateExpressionsOnConfigurable multiple times within loops and recreates the same context instance each time.
      consider pulling out the logic to create a context so that the context instance can be reused.

       protected void performComponentApplyModel(View view, Component component, Object model,
                  Map<String, Integer> visitedIds) {
              if (component == null) {
                  return;
              }
      
              // set context on component for evaluating expressions
              component.pushAllToContext(getCommonContext(view, component));
      
              StandardEvaluationContext context = getExpressionEvaluatorService().getContext(model,component.getContext());
      
              for (PropertyReplacer replacer : component.getPropertyReplacers()) {
                  getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, replacer, context);
              }
      
              for (ComponentModifier modifier : component.getComponentModifiers()) {
                  getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, modifier, context);
              }
      
              getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, component, context);
      
              // evaluate expressions on component security
              ComponentSecurity componentSecurity = component.getComponentSecurity();
              getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, componentSecurity, context);
      
              // evaluate expressions on the binding info object
              if (component instanceof DataBinding) {
                  BindingInfo bindingInfo = ((DataBinding) component).getBindingInfo();
                  getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, bindingInfo, context);
              }
      
              // set context evaluate expressions on the layout manager
              if (component instanceof Container) {
                  LayoutManager layoutManager = ((Container) component).getLayoutManager();
      
                  if (layoutManager != null) {
                      layoutManager.getContext().putAll(getCommonContext(view, component));
                      layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
                      layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.MANAGER, layoutManager);
      
                      getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, layoutManager, model,
                              layoutManager.getContext());
      
                      layoutManager.setId(adjustIdIfNecessary(layoutManager.getId(), visitedIds));
                  }
              }
      
              // sync the component with previous client side state
              syncClientSideStateForComponent(component, ((ViewModel) model).getClientStateForSyncing());
      
              // invoke authorizer and presentation controller to set component state
              applyAuthorizationAndPresentationLogic(view, component, (ViewModel) model);
      
              // adjust ids for duplicates if necessary
              //component.setId(adjustIdIfNecessary(component.getId(), visitedIds));
      
              // invoke component to perform its conditional logic
              Component parent = (Component) component.getContext().get(UifConstants.ContextVariableNames.PARENT);
              component.performApplyModel(view, model, parent);
      
              // invoke service override hook
              performCustomApplyModel(view, component, model);
      
              // invoke component modifiers configured to run in the apply model phase
              runComponentModifiers(view, component, model, UifConstants.ViewPhases.APPLY_MODEL);
      
              // get children and recursively perform conditional logic
              for (Component nestedComponent : component.getComponentsForLifecycle()) {
                  if (nestedComponent != null) {
                      nestedComponent.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
                      performComponentApplyModel(view, nestedComponent, model, visitedIds);
                  }
              }
          }
      

      This class is called dozens of times for each page, especially if there are collection controllers.

        Issue Links

          Activity

          Hide
          Brian Smith (Inactive) added a comment -

          I dont think the 3rd suggestion applies anymore

          Show
          Brian Smith (Inactive) added a comment - I dont think the 3rd suggestion applies anymore

            People

            • Assignee:
              Brian Smith (Inactive)
              Reporter:
              Daniel Epstein (Inactive)
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - 1 hour
                1h
                Remaining:
                Remaining Estimate - 0 minutes
                0m
                Logged:
                Time Spent - 1 hour
                1h

                  Agile

                    Structure Helper Panel