WebWork performance problems on issues Navigator

XMLWordPrintable

      WebWork used strange way to find action - it ask for action handler and look on result - if Exception, it will try to get another action. On everyone request milliards of Exceptions throws for everyone action. We have a patch for it - just caching valid action handling for future get it without exceptions waterfall.

      Look into webwork.properties and replace webwork.configuration.class for Cached configurations:

      #webwork.configuration.class=com.atlassian.jira.config.webwork.ApplicationPropertiesConfiguration,com.atlassian.jira.config.webwork.JiraConfiguration
      webwork.configuration.class=com.atlassian.jira.config.webwork.CachedApplicationPropertiesConfiguration,com.atlassian.jira.config.webwork.CachedJiraConfiguration
      

      Sure, you must implement it:

      package com.atlassian.jira.config.webwork;
      
      import com.atlassian.jira.config.webwork.ApplicationPropertiesConfiguration;
      
      public class CachedApplicationPropertiesConfiguration extends ApplicationPropertiesConfiguration implements DelegatedConfiguration {
          private final WebworkConfigurationCache config = new WebworkConfigurationCache(this);
      
          @Override
          public Object getImpl(String aName) throws IllegalArgumentException {
              return config.getImpl(aName);
          }
      
          @Override
          public void setImpl(String aName, Object aValue) throws IllegalArgumentException, UnsupportedOperationException {
              config.setImpl(aName, aValue);
          }
      
          public Object getDelegateImpl(String aName) {
              return super.getImpl(aName);
          }
      
          public void setDelegateImpl(String aName, Object aValue) {
              super.setImpl(aName, aValue);
          }
      }
      
      package  com.atlassian.jira.config.webwork;
      
      import com.atlassian.jira.config.webwork.JiraConfiguration;
      
      public class CachedJiraConfiguration extends JiraConfiguration implements DelegatedConfiguration {
          private final WebworkConfigurationCache cache = new WebworkConfigurationCache(this);
      
          @Override
          public Object getImpl(String aName) throws IllegalArgumentException {
              return cache.getImpl(aName);
          }
      
          @Override
          public void setImpl(String aName, Object aValue) throws IllegalArgumentException, UnsupportedOperationException {
              cache.setImpl(aName, aValue);
          }
      
          public Object getDelegateImpl(String aName) {
              return super.getImpl(aName);
          }
      
          public void setDelegateImpl(String aName, Object aValue) {
              super.setImpl(aName, aValue);
          }
      }
      
      package com.atlassian.jira.config.webwork;
      
      public interface DelegatedConfiguration {
          Object getDelegateImpl(String aName);
      
          void setDelegateImpl(String aName, Object aValue);
      }
      
      package com.atlassian.jira.config.webwork;
      
      import java.lang.ref.SoftReference;
      import java.util.*;
      
      import org.apache.log4j.Logger;
      
      /**
       * Configuration cache to increase webwork configuration.
       * This method cache all requests to webwork config. The webwork config is used
       * exception flow for "no value" mark. But it bottle neck for JIRA.
       *
       * @author Alexey Efimov
       */
      public class WebworkConfigurationCache {
          private static final List<String> NULLABLE = Arrays.asList(
                  "webwork.util.SubsetIteratorFilter.success",
                  "util.UserAction.success"
          );
          private static final String WEBWORK_ACTION_EXTENSION = "webwork.action.extension";
          private static final String DEFAULT_WEBWORK_ACTION_EXTENSION_VALUE = "jspa";
          private static final String WEBWORK_CONFIGURATION_XML_RELOAD = "webwork.configuration.xml.reload";
          private static final String DEFAULT_WEBWORK_CONFIGURATION_XML_RELOAD_VALUE = Boolean.FALSE.toString();
      
          private static final Logger LOG = Logger.getLogger(WebworkConfigurationCache.class);
          private final DelegatedConfiguration delegate;
          private final Cache[] cache = {new Cache(), new Cache()};
      
          public WebworkConfigurationCache(DelegatedConfiguration delegate) {
              this.delegate = delegate;
          }
      
          public Object getImpl(String aName) throws IllegalArgumentException {
              if (contains(aName)) {
                  return get(aName);
              }
              try {
                  return getAndCache(aName);
              } catch (IllegalArgumentException e) {
                  // This is root of really low performance (see webwork.config.XMLActionConfiguration.getImpl)
                  if (WEBWORK_CONFIGURATION_XML_RELOAD.equals(aName)) {
                      cache(aName, DEFAULT_WEBWORK_CONFIGURATION_XML_RELOAD_VALUE);
                  } else if (WEBWORK_ACTION_EXTENSION.equals(aName)) {
                      cache(aName, DEFAULT_WEBWORK_ACTION_EXTENSION_VALUE);
                  } else if (NULLABLE.contains(aName)) {
                      // This is an approved nullable cache 
                      cache(aName, null);
                  }
                  throw e;
              }
          }
      
          private Object get(String aName) {
              return cache[cache.length - 1].get(aName);
          }
      
          private boolean contains(String aName) {
              return cache[cache.length - 1].contains(aName);
          }
      
          private Object getAndCache(String aName) {
              Object impl = delegate.getDelegateImpl(aName);
              cache(aName, impl);
              return impl;
          }
      
          public void setImpl(String aName, Object aValue) throws IllegalArgumentException, UnsupportedOperationException {
              delegate.setDelegateImpl(aName, aValue);
              cache(aName, aValue);
          }
      
          private Object cache(String aName, Object aValue) {
              // Check existing levels of cache
              int start = 0;
              while (start < cache.length && cache[start].same(aName, aValue)) {
                  start++;
              }
              // Fill first empty level
              if (start < cache.length) {
                  if (start == cache.length - 1) {
                      // Only last level is used for reading from cache
                      LOG.info(String.format("Put to cache key %s and value %s", aName, aValue));
                  }
                  cache[start].set(aName, aValue);
              }
              // And clear last
              for (int i = start + 1; i < cache.length; i++) {
                  cache[i].remove(aName);
              }
              return aValue;
          }
      
          private static final class Cache {
              public final Set<String> nullCache = new HashSet<String>();
              public final Map<String, SoftReference<Object>> cache = new HashMap<String, SoftReference<Object>>();
      
              public boolean contains(String key) {
                  if (nullCache.contains(key)) {
                      return true;
                  }
                  SoftReference<Object> reference = cache.get(key);
                  if (reference != null) {
                      Object v = reference.get();
                      if (v != null) {
                          return true;
                      }
                  }
                  return false;
              }
      
              public boolean same(String key, Object value) {
                  if (contains(key)) {
                      Object v = get(key);
                      return value == null && v == null || value != null && value.equals(v);
                  }
                  return false;
              }
      
              public Object get(String key) {
                  if (nullCache.contains(key)) {
                      return null;
                  }
                  SoftReference<Object> reference = cache.get(key);
                  if (reference != null) {
                      Object v = reference.get();
                      if (v != null) {
                          return v;
                      }
                  }
                  return null;
              }
      
              public void set(String key, Object value) {
                  if (value != null) {
                      nullCache.remove(key);
                      cache.put(key, new SoftReference<Object>(value));
                  } else {
                      cache.remove(key);
                      nullCache.add(key);
                  }
              }
      
              public void remove(String key) {
                  nullCache.remove(key);
                  cache.remove(key);
              }
          }
      }
      

              Assignee:
              Unassigned
              Reporter:
              Alexey Efimov
              Votes:
              1 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved: