From f4b68262cef98b7b043d8100686e6c83980b4a4c Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 23 Dec 2011 15:24:52 +0100 Subject: [PATCH 01/12] put changeSet behind a WeakReference --- .../main/java/hudson/model/AbstractBuild.java | 75 +++++++++++-------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index f90a3beff4d1..88a8082f673d 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -68,6 +68,9 @@ import java.io.File; import java.io.IOException; import java.io.StringWriter; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.text.MessageFormat; import java.util.AbstractSet; import java.util.ArrayList; @@ -124,7 +127,7 @@ public abstract class AbstractBuild

,R extends Abs /** * Changes in this build. */ - private volatile transient ChangeLogSet changeSet; + private volatile transient Reference> changeSet; /** * Cumulative list of people who contributed to the build problem. @@ -280,7 +283,7 @@ public final FilePath getModuleRoot() { public FilePath[] getModuleRoots() { FilePath ws = getWorkspace(); if (ws==null) return null; - return getParent().getScm().getModuleRoots(ws,this); + return getParent().getScm().getModuleRoots(ws, this); } /** @@ -568,10 +571,11 @@ private void checkout(BuildListener listener) throws Exception { SCM scm = project.getScm(); AbstractBuild.this.scm = scm.createChangeLogParser(); - AbstractBuild.this.changeSet = AbstractBuild.this.calcChangeSet(); + ChangeLogSet cs = AbstractBuild.this.calcChangeSet(); + AbstractBuild.this.changeSet = new SoftReference(cs); for (SCMListener l : Jenkins.getInstance().getSCMListeners()) - l.onChangeLogParsed(AbstractBuild.this,listener,changeSet); + l.onChangeLogParsed(AbstractBuild.this,listener,cs); return; } } catch (AbortException e) { @@ -743,6 +747,11 @@ public Collection getBuildFingerprints() { return Collections.emptyList(); } + /* + * No need to to lock the entire AbstractBuild on change set calculcation + */ + private transient Object changeSetLock = new Object(); + /** * Gets the changes incorporated into this build. * @@ -750,34 +759,40 @@ public Collection getBuildFingerprints() { */ @Exported public ChangeLogSet getChangeSet() { - if (scm==null) { - // for historical reason, null means CVS. - try { - Class c = Jenkins.getInstance().getPluginManager().uberClassLoader.loadClass("hudson.scm.CVSChangeLogParser"); - scm = (ChangeLogParser)c.newInstance(); - } catch (ClassNotFoundException e) { - // if CVS isn't available, fall back to something non-null. - scm = new NullChangeLogParser(); - } catch (InstantiationException e) { - scm = new NullChangeLogParser(); - throw (Error)new InstantiationError().initCause(e); - } catch (IllegalAccessException e) { - scm = new NullChangeLogParser(); - throw (Error)new IllegalAccessError().initCause(e); + synchronized (changeSetLock) { + if (scm==null) { + // for historical reason, null means CVS. + try { + Class c = Jenkins.getInstance().getPluginManager().uberClassLoader.loadClass("hudson.scm.CVSChangeLogParser"); + scm = (ChangeLogParser)c.newInstance(); + } catch (ClassNotFoundException e) { + // if CVS isn't available, fall back to something non-null. + scm = NullChangeLogParser.INSTANCE; + } catch (InstantiationException e) { + scm = NullChangeLogParser.INSTANCE; + throw (Error)new InstantiationError().initCause(e); + } catch (IllegalAccessException e) { + scm = NullChangeLogParser.INSTANCE; + throw (Error)new IllegalAccessError().initCause(e); + } } - } - - if (changeSet==null) // cached value - try { - changeSet = calcChangeSet(); - } finally { - // defensive check. if the calculation fails (such as through an exception), - // set a dummy value so that it'll work the next time. the exception will - // be still reported, giving the plugin developer an opportunity to fix it. - if (changeSet==null) - changeSet=ChangeLogSet.createEmpty(this); + + ChangeLogSet result = changeSet != null ? changeSet.get() : null; + if (result == null) { + try { + result = calcChangeSet(); + } finally { + // defensive check. if the calculation fails (such as through an exception), + // set a dummy value so that it'll work the next time. the exception will + // be still reported, giving the plugin developer an opportunity to fix it. + if (result == null) { + result = ChangeLogSet.createEmpty(this); + } + } + changeSet = new WeakReference>(result); } - return changeSet; + return result; + } } /** From 39c76672607a6b7ad0cc9b6c35507a2fe9a4b8db Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 23 Dec 2011 16:42:42 +0100 Subject: [PATCH 02/12] singleton NullChangeLogParser to reduce memory footprint --- core/src/main/java/hudson/model/AbstractBuild.java | 2 +- core/src/main/java/hudson/scm/NullChangeLogParser.java | 7 +++++++ core/src/main/java/hudson/scm/NullSCM.java | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) mode change 100644 => 100755 core/src/main/java/hudson/scm/NullChangeLogParser.java mode change 100644 => 100755 core/src/main/java/hudson/scm/NullSCM.java diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index 88a8082f673d..d93eff1449f5 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -563,7 +563,7 @@ private void checkout(BuildListener listener) throws Exception { // for historical reasons, null in the scm field means CVS, so we need to explicitly set this to something // in case check out fails and leaves a broken changelog.xml behind. // see http://www.nabble.com/CVSChangeLogSet.parse-yields-SAXParseExceptions-when-parsing-bad-*AccuRev*-changelog.xml-files-td22213663.html - AbstractBuild.this.scm = new NullChangeLogParser(); + AbstractBuild.this.scm = NullChangeLogParser.INSTANCE; try { if (project.checkout(AbstractBuild.this,launcher,listener,new File(getRootDir(),"changelog.xml"))) { diff --git a/core/src/main/java/hudson/scm/NullChangeLogParser.java b/core/src/main/java/hudson/scm/NullChangeLogParser.java old mode 100644 new mode 100755 index 976d4438646e..1c40e52d7ffd --- a/core/src/main/java/hudson/scm/NullChangeLogParser.java +++ b/core/src/main/java/hudson/scm/NullChangeLogParser.java @@ -34,7 +34,14 @@ * @author Kohsuke Kawaguchi */ public class NullChangeLogParser extends ChangeLogParser { + + public static final NullChangeLogParser INSTANCE = new NullChangeLogParser(); + public ChangeLogSet parse(AbstractBuild build, File changelogFile) throws IOException, SAXException { return ChangeLogSet.createEmpty(build); } + + public Object readResolve() { + return INSTANCE; + } } diff --git a/core/src/main/java/hudson/scm/NullSCM.java b/core/src/main/java/hudson/scm/NullSCM.java old mode 100644 new mode 100755 index 419c5fbe3e3a..9db1b8eb00db --- a/core/src/main/java/hudson/scm/NullSCM.java +++ b/core/src/main/java/hudson/scm/NullSCM.java @@ -55,7 +55,7 @@ public boolean checkout(AbstractBuild build, Launcher launcher, FilePath re } public ChangeLogParser createChangeLogParser() { - return new NullChangeLogParser(); + return NullChangeLogParser.INSTANCE; } @Extension From ac905ff0dcc8766532fafe3eac9101405d8416f1 Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 23 Dec 2011 17:18:15 +0100 Subject: [PATCH 03/12] faster lookup by node name for NodeList --- core/src/main/java/hudson/model/Hudson.java | 2 +- .../src/main/java/hudson/slaves/NodeList.java | 80 ++++++++++++++++++- core/src/main/java/jenkins/model/Jenkins.java | 13 +-- .../test/java/hudson/slaves/NodeListTest.java | 8 +- 4 files changed, 83 insertions(+), 20 deletions(-) mode change 100644 => 100755 core/src/main/java/hudson/slaves/NodeList.java mode change 100644 => 100755 core/src/main/java/jenkins/model/Jenkins.java diff --git a/core/src/main/java/hudson/model/Hudson.java b/core/src/main/java/hudson/model/Hudson.java index b80f600a64d1..aff8df4341c0 100644 --- a/core/src/main/java/hudson/model/Hudson.java +++ b/core/src/main/java/hudson/model/Hudson.java @@ -119,7 +119,7 @@ public Slave getSlave(String name) { * Use {@link #getNodes()}. Since 1.252. */ public List getSlaves() { - return (List)Collections.unmodifiableList(slaves); + return (List)slaves; } /** diff --git a/core/src/main/java/hudson/slaves/NodeList.java b/core/src/main/java/hudson/slaves/NodeList.java old mode 100644 new mode 100755 index abfbc8ee485b..bb96fed00611 --- a/core/src/main/java/hudson/slaves/NodeList.java +++ b/core/src/main/java/hudson/slaves/NodeList.java @@ -33,8 +33,11 @@ import hudson.util.RobustCollectionConverter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -43,22 +46,91 @@ * * @author Kohsuke Kawaguchi */ -public final class NodeList extends CopyOnWriteArrayList { +public final class NodeList extends ArrayList { + + private Map map = new HashMap(); + public NodeList() { } public NodeList(Collection c) { super(c); + for (Node node: c) { + if (map.put(node.getNodeName(), node) != null) { + // make sure that all names are unique + throw new IllegalArgumentException(node.getNodeName()+" is defined more than once"); + } + } + } + + public NodeList(Node... toCopyIn) { + this(Arrays.asList(toCopyIn)); + } + + public Node getNode(String nodeName) { + return map.get(nodeName); + } + + + @Override + public void add(int index, Node element) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public Node remove(int index) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public void clear() { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + protected void removeRange(int fromIndex, int toIndex) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException("unmodifiable list"); + } + + @Override + public boolean add(Node node) { + throw new UnsupportedOperationException("unmodifiable list"); } - public NodeList(Node[] toCopyIn) { - super(toCopyIn); + @Override + public Node set(int index, Node element) { + throw new UnsupportedOperationException("unmodifiable list"); } /** * {@link Converter} implementation for XStream. * - * Serializaion form is compatible with plain {@link List}. + * Serialization form is compatible with plain {@link List}. */ public static final class ConverterImpl extends RobustCollectionConverter { public ConverterImpl(XStream xstream) { diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java old mode 100644 new mode 100755 index c21880860f24..2d01bc00f8be --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -1563,11 +1563,7 @@ public JDK getJDK(String name) { * Gets the slave node of the give name, hooked under this Hudson. */ public Node getNode(String name) { - for (Node s : getNodes()) { - if(s.getNodeName().equals(name)) - return s; - } - return null; + return slaves.getNode(name); } /** @@ -1586,7 +1582,7 @@ protected Map getComputerMap() { * represents the master. */ public List getNodes() { - return Collections.unmodifiableList(slaves); + return slaves; } /** @@ -1614,11 +1610,6 @@ public synchronized void removeNode(Node n) throws IOException { } public void setNodes(List nodes) throws IOException { - // make sure that all names are unique - Set names = new HashSet(); - for (Node n : nodes) - if(!names.add(n.getNodeName())) - throw new IllegalArgumentException(n.getNodeName()+" is defined more than once"); this.slaves = new NodeList(nodes); updateComputerList(); trimLabels(); diff --git a/core/src/test/java/hudson/slaves/NodeListTest.java b/core/src/test/java/hudson/slaves/NodeListTest.java index 042da99d6c64..cdd2f9a1fe6c 100644 --- a/core/src/test/java/hudson/slaves/NodeListTest.java +++ b/core/src/test/java/hudson/slaves/NodeListTest.java @@ -39,6 +39,7 @@ import java.io.File; import java.io.IOException; +import java.util.Random; import java.util.Set; import org.apache.commons.io.FileUtils; @@ -48,8 +49,9 @@ */ public class NodeListTest extends TestCase { static class DummyNode extends Node { + String nodeName = Long.toString(new Random().nextLong()); public String getNodeName() { - throw new UnsupportedOperationException(); + return nodeName; } public void setNodeName(String name) { @@ -112,9 +114,7 @@ public Node asNode() { } public void testSerialization() throws Exception { - NodeList nl = new NodeList(); - nl.add(new DummyNode()); - nl.add(new EphemeralNode()); + NodeList nl = new NodeList(new DummyNode(), new EphemeralNode()); File tmp = File.createTempFile("test","test"); try { From 3cfd2ef87cfa5766a8d1b5949c6f42f8bb93fa31 Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 23 Dec 2011 17:44:48 +0100 Subject: [PATCH 04/12] support @XstreamConverter annotation --- .../java/hudson/util/LRUStringConverter.java | 41 +++++++++++++++++++ .../util/RobustReflectionConverter.java | 6 ++- 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100755 core/src/main/java/hudson/util/LRUStringConverter.java mode change 100644 => 100755 core/src/main/java/hudson/util/RobustReflectionConverter.java diff --git a/core/src/main/java/hudson/util/LRUStringConverter.java b/core/src/main/java/hudson/util/LRUStringConverter.java new file mode 100755 index 000000000000..4be36ab0d737 --- /dev/null +++ b/core/src/main/java/hudson/util/LRUStringConverter.java @@ -0,0 +1,41 @@ +package hudson.util; + +import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; +import com.thoughtworks.xstream.converters.basic.StringConverter; +import org.apache.commons.collections.map.LRUMap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class LRUStringConverter extends AbstractSingleValueConverter { + + /** + * A Map to store strings as long as needed to map similar strings onto the same instance and conserve memory. The + * map can be set from the outside during construction, so it can be a LRU map or a weak map, synchronised or not. + */ + private final Map cache; + + public LRUStringConverter() { + this(1000); + } + + public LRUStringConverter(int size) { + cache = Collections.synchronizedMap(new LRUMap(size)); + } + + public boolean canConvert(final Class type) { + return type.equals(String.class); + } + + public Object fromString(final String str) { + String s = cache.get(str); + + if (s == null) { + cache.put(str, str); + s = str; + } + + return s; + } +} \ No newline at end of file diff --git a/core/src/main/java/hudson/util/RobustReflectionConverter.java b/core/src/main/java/hudson/util/RobustReflectionConverter.java old mode 100644 new mode 100755 index 7afc7e10557d..25baf8bcdf6b --- a/core/src/main/java/hudson/util/RobustReflectionConverter.java +++ b/core/src/main/java/hudson/util/RobustReflectionConverter.java @@ -172,7 +172,8 @@ private void writeField(String fieldName, String aliasName, Class fieldType, Cla } protected void marshallField(final MarshallingContext context, Object newObj, Field field) { - context.convertAnother(newObj); + Converter converter = mapper.getLocalConverter(field.getDeclaringClass(), field.getName()); + context.convertAnother(newObj, converter); } public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { @@ -287,7 +288,8 @@ private boolean fieldDefinedInClass(Object result, String attrName) { } protected Object unmarshalField(final UnmarshallingContext context, final Object result, Class type, Field field) { - return context.convertAnother(result, type); + Converter converter = mapper.getLocalConverter(field.getDeclaringClass(), field.getName()); + return context.convertAnother(result, type, converter); } private Map writeValueToImplicitCollection(UnmarshallingContext context, Object value, Map implicitCollections, Object result, String itemFieldName) { From 5f64e483e9375632c71e94b927a400173897d35c Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 6 Jan 2012 22:47:56 +0100 Subject: [PATCH 05/12] User.getBuilds() is crazy-expensive, since it iterates over all builds. Restrict it to the most recent builds. --- core/src/main/java/hudson/model/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index 76a37fc38060..62a8065ed6bc 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -384,7 +384,7 @@ public String getDisplayName() { public RunList getBuilds() { List r = new ArrayList(); for (AbstractProject p : Jenkins.getInstance().getAllItems(AbstractProject.class)) - for (AbstractBuild b : p.getBuilds()) + for (AbstractBuild b : p.getBuilds().newBuilds()) if(b.hasParticipant(this)) r.add(b); return RunList.fromRuns(r); From 53eb2001bc53d9c6f9562530ab6f7ccef6f7ea07 Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 23 Dec 2011 17:53:28 +0100 Subject: [PATCH 06/12] use LRU map for certain strings --- core/src/main/java/hudson/tasks/Fingerprinter.java | 3 +-- core/src/main/java/hudson/tasks/MailMessageIdAction.java | 6 ++++++ .../main/java/hudson/maven/reporters/MavenArtifact.java | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) mode change 100644 => 100755 core/src/main/java/hudson/tasks/MailMessageIdAction.java mode change 100644 => 100755 maven-plugin/src/main/java/hudson/maven/reporters/MavenArtifact.java diff --git a/core/src/main/java/hudson/tasks/Fingerprinter.java b/core/src/main/java/hudson/tasks/Fingerprinter.java index d62416b7a999..d94bc0f0cc89 100644 --- a/core/src/main/java/hudson/tasks/Fingerprinter.java +++ b/core/src/main/java/hudson/tasks/Fingerprinter.java @@ -34,7 +34,6 @@ import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Action; -import hudson.model.Build; import hudson.model.BuildListener; import hudson.model.DependecyDeclarer; import hudson.model.DependencyGraph; @@ -65,7 +64,6 @@ import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -294,6 +292,7 @@ public boolean isApplicable(Class jobType) { * Action for displaying fingerprints. */ public static final class FingerprintAction implements RunAction { + private final AbstractBuild build; /** diff --git a/core/src/main/java/hudson/tasks/MailMessageIdAction.java b/core/src/main/java/hudson/tasks/MailMessageIdAction.java old mode 100644 new mode 100755 index a63855f63e74..106df969f9da --- a/core/src/main/java/hudson/tasks/MailMessageIdAction.java +++ b/core/src/main/java/hudson/tasks/MailMessageIdAction.java @@ -24,6 +24,7 @@ package hudson.tasks; import hudson.model.Action; +import hudson.model.Run; /** * Remembers the message ID of the e-mail that was sent for the build. @@ -34,6 +35,11 @@ * @author Kohsuke Kawaguchi */ public class MailMessageIdAction implements Action { + + static { + Run.XSTREAM.processAnnotations(MailMessageIdAction.class); + } + /** * Message ID of the e-mail sent for the build. */ diff --git a/maven-plugin/src/main/java/hudson/maven/reporters/MavenArtifact.java b/maven-plugin/src/main/java/hudson/maven/reporters/MavenArtifact.java old mode 100644 new mode 100755 index a7f4537e0169..e24c65aab1a8 --- a/maven-plugin/src/main/java/hudson/maven/reporters/MavenArtifact.java +++ b/maven-plugin/src/main/java/hudson/maven/reporters/MavenArtifact.java @@ -30,6 +30,8 @@ import hudson.model.Api; import hudson.model.BuildListener; import hudson.model.FingerprintMap; +import hudson.model.Run; +import hudson.util.LRUStringConverter; import jenkins.model.Jenkins; import hudson.util.HttpResponses; @@ -68,6 +70,11 @@ */ @ExportedBean public final class MavenArtifact implements Serializable { + + static { + Run.XSTREAM.registerLocalConverter(MavenArtifact.class, "md5sum", new LRUStringConverter(5000)); + } + /** * Basic parameters of a Maven artifact. */ From b0e71702968bed783c9d1f51281c9f37eeadac9f Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Sat, 26 Feb 2011 22:15:59 +0100 Subject: [PATCH 07/12] use immutable collection for AbstractBuild.culprits Conflicts: core/src/main/java/hudson/model/AbstractBuild.java --- core/src/main/java/hudson/model/AbstractBuild.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/model/AbstractBuild.java b/core/src/main/java/hudson/model/AbstractBuild.java index d93eff1449f5..376111faaacd 100644 --- a/core/src/main/java/hudson/model/AbstractBuild.java +++ b/core/src/main/java/hudson/model/AbstractBuild.java @@ -23,6 +23,7 @@ */ package hudson.model; +import com.google.common.collect.ImmutableSortedSet; import hudson.AbortException; import hudson.EnvVars; import hudson.Functions; @@ -628,7 +629,7 @@ public final void post(BuildListener listener) throws Exception { HashSet r = new HashSet(); for (User u : getCulprits()) r.add(u.getId()); - culprits = r; + culprits = ImmutableSortedSet.copyOf(r); CheckPoint.CULPRITS_DETERMINED.report(); } } From 88360dbbbe033b84010b8a92966739cd694d6833 Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Sat, 26 Feb 2011 22:17:03 +0100 Subject: [PATCH 08/12] reduce footprint on TestResultAction.testData --- core/src/main/java/hudson/tasks/junit/TestResultAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/tasks/junit/TestResultAction.java b/core/src/main/java/hudson/tasks/junit/TestResultAction.java index 18ed232b8e06..c27053e7a34a 100644 --- a/core/src/main/java/hudson/tasks/junit/TestResultAction.java +++ b/core/src/main/java/hudson/tasks/junit/TestResultAction.java @@ -193,7 +193,7 @@ public static abstract class Data { public Object readResolve() { super.readResolve(); // let it do the post-deserialization work if (testData == null) { - testData = new ArrayList(); + testData = new ArrayList(0); } return this; From 97357d8a16cf65a4350b93e8f3ca0fec8a193ddb Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 6 Jan 2012 22:44:10 +0100 Subject: [PATCH 09/12] mkdirs() is expensive on Windows, so don't do it on every getRootDir() --- core/src/main/java/hudson/model/Run.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/hudson/model/Run.java b/core/src/main/java/hudson/model/Run.java index 784e75842b3a..b1ee05efc545 100644 --- a/core/src/main/java/hudson/model/Run.java +++ b/core/src/main/java/hudson/model/Run.java @@ -261,6 +261,7 @@ protected Run(JobT job, long timestamp) { this.project = job; this.timestamp = timestamp; this.state = State.NOT_STARTED; + getRootDir().mkdirs(); } /** @@ -828,9 +829,7 @@ public Descriptor getDescriptorByName(String className) { * Files related to this {@link Run} should be stored below this directory. */ public File getRootDir() { - File f = new File(project.getBuildDir(),getId()); - f.mkdirs(); - return f; + return new File(project.getBuildDir(),getId()); } /** From cb1248e36b4f3453be34fc67ea23ff95badc664f Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 6 Jan 2012 22:55:55 +0100 Subject: [PATCH 10/12] we shouldn't try to agressively load changesets, since they are now lazily loaded Conflicts: core/src/main/java/hudson/WebAppMain.java --- core/src/main/java/hudson/WebAppMain.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/core/src/main/java/hudson/WebAppMain.java b/core/src/main/java/hudson/WebAppMain.java index 722b0391b5a1..34f305d68dcb 100644 --- a/core/src/main/java/hudson/WebAppMain.java +++ b/core/src/main/java/hudson/WebAppMain.java @@ -217,15 +217,6 @@ public void run() { Jenkins instance = new Hudson(home, context); context.setAttribute(APP, instance); - // trigger the loading of changelogs in the background, - // but give the system 10 seconds so that the first page - // can be served quickly - Trigger.timer.schedule(new SafeTimerTask() { - public void doRun() { - User.getUnknown().getBuilds(); - } - }, 1000*10); - // at this point we are open for business and serving requests normally LOGGER.info("Jenkins is fully up and running"); success = true; From 0f4f69d130289bd3a28981697523120f58ddb7d0 Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Fri, 6 Jan 2012 22:59:53 +0100 Subject: [PATCH 11/12] Jenkins.getItems() is quite expensive with a lot of items in an access controled environment. Let's try to avoid access control checks for some well known AuthorizationStrategies --- core/src/main/java/jenkins/model/Jenkins.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 2d01bc00f8be..3d29ae7de728 100755 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -132,6 +132,7 @@ import hudson.security.AuthorizationStrategy; import hudson.security.BasicAuthenticationFilter; import hudson.security.FederatedLoginService; +import hudson.security.FullControlOnceLoggedInAuthorizationStrategy; import hudson.security.HudsonFilter; import hudson.security.LegacyAuthorizationStrategy; import hudson.security.LegacySecurityRealm; @@ -1252,12 +1253,17 @@ public List getActions() { */ @Exported(name="jobs") public List getItems() { + if (authorizationStrategy instanceof AuthorizationStrategy.Unsecured || + authorizationStrategy instanceof FullControlOnceLoggedInAuthorizationStrategy) { + return new ArrayList(items.values()); + } + List viewableItems = new ArrayList(); for (TopLevelItem item : items.values()) { if (item.hasPermission(Item.READ)) viewableItems.add(item); } - + return viewableItems; } @@ -2617,9 +2623,13 @@ public Map> getAllThreadDumps() throws IOException, In // issue the requests all at once Map>> future = new HashMap>>(); + for (Computer c : getComputers()) { future.put(c.getName(), RemotingDiagnostics.getThreadDumpAsync(c.getChannel())); } + if (toComputer() == null) { + future.put("master", RemotingDiagnostics.getThreadDumpAsync(MasterComputer.localChannel)); + } // if the result isn't available in 5 sec, ignore that. // this is a precaution against hang nodes @@ -3493,7 +3503,7 @@ public static T lookup(Class type) { */ public static final XStream2 XSTREAM2 = (XStream2)XSTREAM; - private static final int TWICE_CPU_NUM = Runtime.getRuntime().availableProcessors() * 2; + private static final int TWICE_CPU_NUM = Math.max(4, Runtime.getRuntime().availableProcessors() * 2); /** * Thread pool used to load configuration in parallel, to improve the start up time. From 58e0ea4437d41a48462cd0292cb590286c9a782a Mon Sep 17 00:00:00 2001 From: Tom Huybrechts Date: Sat, 7 Jan 2012 14:01:07 +0100 Subject: [PATCH 12/12] LRU cache on MailMessageIdAction.messageId --- core/src/main/java/hudson/tasks/MailMessageIdAction.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/hudson/tasks/MailMessageIdAction.java b/core/src/main/java/hudson/tasks/MailMessageIdAction.java index 106df969f9da..c217acb75bab 100755 --- a/core/src/main/java/hudson/tasks/MailMessageIdAction.java +++ b/core/src/main/java/hudson/tasks/MailMessageIdAction.java @@ -23,8 +23,10 @@ */ package hudson.tasks; +import com.thoughtworks.xstream.annotations.XStreamConverter; import hudson.model.Action; import hudson.model.Run; +import hudson.util.xstream.LRUStringConverter; /** * Remembers the message ID of the e-mail that was sent for the build. @@ -43,6 +45,7 @@ public class MailMessageIdAction implements Action { /** * Message ID of the e-mail sent for the build. */ + @XStreamConverter(LRUStringConverter.class) public final String messageId; public MailMessageIdAction(String messageId) {