Uploaded image for project: 'Jira Data Center'
  1. Jira Data Center
  2. JRASERVER-15246

WebWork performance problems on issues Navigator

    XMLWordPrintable

Details

    • We collect Jira feedback from various sources, and we evaluate what we've collected when planning our product roadmap. To understand how this piece of feedback will be reviewed, see our Implementation of New Features Policy.

    Description

      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);
              }
          }
      }
      

      Attachments

        Activity

          People

            Unassigned Unassigned
            3652ed9ede2e Alexey Efimov
            Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: